From 25451e0b6b26bd76acb2d78a3a0be82bdec9d605 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Wed, 19 Apr 2017 13:53:47 -0500 Subject: [PATCH] Progress: improve language support when using global data, add headers in some tests programs, and fix language related issues When I moved languages into the global defaults class, I missed a few chunks of code that needed to be updated. I noticed that some of the tests were not printing any headers. Allow for setting content-language with multiple languages, as is the case with one of the tests using japanese and english. --- common/base/classes/base_defaults_global.php | 25 +++++- common/base/classes/base_http.php | 110 +++++++++++++++++---------- common/theme/classes/theme_html.php | 12 +-- examples/http.php | 11 ++- examples/test.php | 37 ++++++++- program/reservation/index.php | 4 + 6 files changed, 146 insertions(+), 53 deletions(-) diff --git a/common/base/classes/base_defaults_global.php b/common/base/classes/base_defaults_global.php index a6769bf..023e53a 100644 --- a/common/base/classes/base_defaults_global.php +++ b/common/base/classes/base_defaults_global.php @@ -44,7 +44,7 @@ class c_base_defaults_global { const ERROR_BACKTRACE_ARGUMENTS = FALSE; // provide a language to fallback to if none is set. - const LANGUAGE_CLASS_DEFAULT = 'c_base_language_limited'; + const LANGUAGE_CLASS_DEFAULT = 'c_base_language_us_only'; /** @@ -263,7 +263,7 @@ class c_base_defaults_global { * * @see: i_base_language */ - public static function s_set_language($language) { + public static function s_set_languages($language) { if (!($language instanceof i_base_language)) { $error = c_base_error::s_log(NULL, array('arguments' => array(':argument_name' => 'language', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::INVALID_ARGUMENT); return c_base_return_error::s_false($error); @@ -281,7 +281,7 @@ class c_base_defaults_global { * * @see: i_base_language */ - public static function s_get_language() { + public static function s_get_languages() { if (is_null(self::$s_language)) { $class = self::LANGUAGE_CLASS_DEFAULT; self::$s_language = new $class(); @@ -290,4 +290,23 @@ class c_base_defaults_global { return self::$s_language; } + + /** + * Get the name of the currently assigned language class. + * + * @return string + * A string representing the class of the language. + * + * @see: i_base_language + */ + public static function s_get_languages_class() { + if (is_null(self::$s_language)) { + $class = self::LANGUAGE_CLASS_DEFAULT; + self::$s_language = new $class(); + return c_base_return_string::s_new($class); + } + + $class = get_class($this->s_language); + return c_base_return_string::s_new($class); + } } diff --git a/common/base/classes/base_http.php b/common/base/classes/base_http.php index 131f26b..9b120ed 100644 --- a/common/base/classes/base_http.php +++ b/common/base/classes/base_http.php @@ -239,7 +239,7 @@ class c_base_http extends c_base_rfc_string { private $content_is_file; private $buffer_enabled; - private $language_class; + private $languages; /** * Class constructor. @@ -257,7 +257,7 @@ class c_base_http extends c_base_rfc_string { $this->content_is_file = NULL; $this->buffer_enabled = FALSE; - $this->language_class = NULL; + $this->languages = NULL; } /** @@ -274,7 +274,7 @@ class c_base_http extends c_base_rfc_string { unset($this->content_is_file); unset($this->buffer_enabled); - unset($this->language_class); + unset($this->languages); parent::__destruct(); } @@ -439,39 +439,47 @@ class c_base_http extends c_base_rfc_string { } /** - * Assign the class name as the language class string. + * Assign the languages class. * - * @param string $class_name + * The languages class provides a list of supported languages. + * + * @param string|i_base_language $class_name * A string name representing an object that is a sub-class of i_base_language. + * Or a language class object. * * @return c_base_return_status * TRUE on success, FALSE otherwise. * FALSE with error bit set is returned on error. */ - public function set_language_class($class_name) { - if (!is_string($class_name) || !is_subclass_of('i_base_language', $class_name) ) { + public function set_languages($class_name) { + if (!(is_string($class_name) && is_subclass_of('i_base_language', $class_name)) || !(is_object($class_name) && $class_name instanceof i_base_language)) { $error = c_base_error::s_log(NULL, array('arguments' => array(':argument_name' => 'class_name', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::INVALID_ARGUMENT); return c_base_return_error::s_false($error); } - $this->language_class = $class_name; + if (is_string($class_name)) { + $this->languages = new $class_name(); + } + else { + $this->languages = $class_name; + } + return new c_base_return_true(); } /** - * Get the language class string currently assigned to this class. + * Get the language class object currently assigned to this class. * - * @return c_base_return_string - * The language class string. - * FALSE with error bit set is returned on error. + * @return i_base_language + * The language class object. */ - public function get_language_class() { - if (is_null($this->language_class)) { - // provide us-only as a failsafe/fallback. - $this->language_class = 'c_base_language_us_only'; + public function get_languages() { + if (is_null($this->languages)) { + // provide a failsafe/fallback. + $this->languages = c_base_defaults_global::s_get_languages(); } - return c_base_return_string::s_new($this->language_class); + return $this->languages; } /** @@ -1781,6 +1789,10 @@ class c_base_http extends c_base_rfc_string { * The language code to assign to the specified header. * If NULL, then the default language according the the given language class is used. * If NULL and the default language is not set, then FALSE with error bit set is returned. + * @param bool $append + * (optional) If TRUE, then append the header name. + * If FALSE, then assign the header name. + * When $language is NULL, $append is treated as FALSE. * * @return c_base_return_status * TRUE on success, FALSE otherwise. @@ -1788,37 +1800,44 @@ class c_base_http extends c_base_rfc_string { * * @see: https://tools.ietf.org/html/rfc7231#section-3.1.3.2 */ - public function set_response_content_language($language = NULL) { + public function set_response_content_language($language = NULL, $append = TRUE) { if (!is_null($language) && !is_int($language)) { $error = c_base_error::s_log(NULL, array('arguments' => array(':argument_name' => 'language', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::INVALID_ARGUMENT); return c_base_return_error::s_false($error); } + if (!is_bool($append)) { + $error = c_base_error::s_log(NULL, array('arguments' => array(':argument_name' => 'append', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::INVALID_ARGUMENT); + return c_base_return_error::s_false($error); + } - if (is_null($language)) { - if (!is_object($this->language_class) || !($this->language_class instanceof i_base_language)) { - $error = c_base_error::s_log(NULL, array('arguments' => array(':variable_name' => 'this->language_class', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::INVALID_VARIABLE); - return c_base_return_error::s_false($error); - } + if (!is_object($this->languages)) { + $this->languages = c_base_defaults_global::s_get_languages(); + } - $default = $this->language_class->s_get_default_id(); + if (is_null($language)) { + $default = $this->languages->s_get_default_id(); if ($default instanceof c_base_return_false) { unset($default); - $error = c_base_error::s_log(NULL, array('arguments' => array(':operation_name' => 'this->language_class->s_get_default_id', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::OPERATION_FAILURE); + $error = c_base_error::s_log(NULL, array('arguments' => array(':operation_name' => 'this->languages->s_get_default_id', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::OPERATION_FAILURE); return c_base_return_error::s_false($error); } - $this->response[self::RESPONSE_CONTENT_LANGUAGE] = $default; + $this->response[self::RESPONSE_CONTENT_LANGUAGE] = array($default->get_value_exact()); unset($default); } else { - if ($language_class->s_get_names_by_id($language) instanceof c_base_return_false) { - $error = c_base_error::s_log(NULL, array('arguments' => array(':operation_name' => 'language_class->s_get_names_by_id', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::OPERATION_FAILURE); + if ($this->languages->s_get_names_by_id($language) instanceof c_base_return_false) { + $error = c_base_error::s_log(NULL, array('arguments' => array(':operation_name' => 'this->languages->s_get_names_by_id', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::OPERATION_FAILURE); return c_base_return_error::s_false($error); } - $this->response[self::RESPONSE_CONTENT_LANGUAGE] = $language; + if (!isset($this->response[self::RESPONSE_CONTENT_LANGUAGE])) { + $this->response[self::RESPONSE_CONTENT_LANGUAGE] = array(); + } + + $this->response[self::RESPONSE_CONTENT_LANGUAGE][] = $language; } return new c_base_return_true(); @@ -3787,19 +3806,19 @@ class c_base_http extends c_base_rfc_string { /** * Obtain HTTP response header: content-language. * - * @return c_base_return_int - * An integer representing the content length value. - * 0 with error bit set is returned on error, including when the key is not defined. + * @return c_base_return_array + * An array of integers representing the content language value. + * An empty array with error bit set is returned on error, including when the key is not defined. * * @see: https://tools.ietf.org/html/rfc7231#section-3.1.3.2 */ public function get_response_content_language() { if (!array_key_exists(self::RESPONSE_CONTENT_LANGUAGE, $this->response)) { $error = c_base_error::s_log(NULL, array('arguments' => array(':index_name' => self::RESPONSE_CONTENT_LANGUAGE, ':array_name' => 'this->response', ':function_name' => __CLASS__ . '->' . __FUNCTION__)), i_base_error_messages::NOT_FOUND_ARRAY_INDEX); - return c_base_return_error::s_value(0, 'c_base_return_int', $error); + return c_base_return_error::s_value(array(), 'c_base_return_array', $error); } - return c_base_return_int::s_new($this->response[self::RESPONSE_CONTENT_LANGUAGE]); + return c_base_return_array::s_new($this->response[self::RESPONSE_CONTENT_LANGUAGE]); } /** @@ -5007,7 +5026,7 @@ class c_base_http extends c_base_rfc_string { // convert the known values into integers for improved processing. foreach ($this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['choices'] as $weight => &$choice) { foreach ($choice as $key => &$c) { - $id = c_base_defaults_global::s_get_language()->s_get_id_by_name($c['choice']); + $id = c_base_defaults_global::s_get_languages()->s_get_id_by_name($c['choice']); if ($id instanceof c_base_return_false) { $c['language'] = NULL; } @@ -8596,15 +8615,24 @@ class c_base_http extends c_base_rfc_string { return; } - $language_array = $this->language_class->s_get_aliases_by_id($this->response[self::RESPONSE_CONTENT_LANGUAGE]); - if ($language_array instanceof c_base_return_array) { - $language_array = $language_array->get_value_exact(); + $output = NULL; + foreach ($this->response[self::RESPONSE_CONTENT_LANGUAGE] as $language) { + $language_array = $this->languages->s_get_aliases_by_id($language); + if ($language_array instanceof c_base_return_array) { + $language_array = $language_array->get_value_exact(); - if (!empty($language_array[0])) { - $header_output[self::RESPONSE_CONTENT_LANGUAGE] = $header_name . ': ' . $language_array[0]; + if (!empty($language_array[0])) { + $output .= ', ' . $language_array[0]; + } } + unset($language_array); + } + unset($language); + + if (!is_null($output)) { + $header_output[self::RESPONSE_CONTENT_LANGUAGE] = $header_name . ': ' . substr($output, 2); } - unset($language_array); + unset($output); } /** diff --git a/common/theme/classes/theme_html.php b/common/theme/classes/theme_html.php index df5bcba..2085a46 100644 --- a/common/theme/classes/theme_html.php +++ b/common/theme/classes/theme_html.php @@ -1475,7 +1475,7 @@ class c_theme_html extends c_base_return { } if (!empty($attribute)) { - $language_array = c_base_defaults_global::s_get_language()->s_get_aliases_by_id($attribute)->get_value_exact(); + $language_array = c_base_defaults_global::s_get_languages()->s_get_aliases_by_id($attribute)->get_value_exact(); // use the first language alias available. $language = array_pop($language_array); @@ -1830,7 +1830,7 @@ class c_theme_html extends c_base_return { // attribute: lang $attribute = $this->html->get_attribute_body(c_base_markup_attributes::ATTRIBUTE_LANGUAGE)->get_value_exact(); if (!empty($attribute)) { - $language_array = c_base_defaults_global::s_get_language()->s_get_aliases_by_id($attribute)->get_value_exact(); + $language_array = c_base_defaults_global::s_get_languages()->s_get_aliases_by_id($attribute)->get_value_exact(); // use the first language alias available. $language = array_pop($language_array); @@ -3057,7 +3057,7 @@ class c_theme_html extends c_base_return { $attribute = $tag->get_attribute(c_base_markup_attributes::ATTRIBUTE_HREF_LANGUAGE)->get_value_exact(); if (!empty($attribute)) { - $language_array = c_base_defaults_global::s_get_language()->s_get_aliases_by_id($attribute)->get_value_exact(); + $language_array = c_base_defaults_global::s_get_languages()->s_get_aliases_by_id($attribute)->get_value_exact(); // use the first language alias available. $language = array_pop($language_array); @@ -3433,7 +3433,7 @@ class c_theme_html extends c_base_return { $attribute = $tag->get_attribute(c_base_markup_attributes::ATTRIBUTE_HREF_LANGUAGE)->get_value_exact(); if (!empty($attribute)) { - $language_array = c_base_defaults_global::s_get_language()->s_get_aliases_by_id($attribute)->get_value_exact(); + $language_array = c_base_defaults_global::s_get_languages()->s_get_aliases_by_id($attribute)->get_value_exact(); // use the first language alias available. $language = array_pop($language_array); @@ -5130,7 +5130,7 @@ class c_theme_html extends c_base_return { $attribute = $tag->get_attribute(c_base_markup_attributes::ATTRIBUTE_HREF_LANGUAGE)->get_value_exact(); if (!empty($attribute)) { - $language_array = c_base_defaults_global::s_get_language()->s_get_aliases_by_id($attribute)->get_value_exact(); + $language_array = c_base_defaults_global::s_get_languages()->s_get_aliases_by_id($attribute)->get_value_exact(); // use the first language alias available. $language = array_pop($language_array); @@ -6840,7 +6840,7 @@ class c_theme_html extends c_base_return { $attribute = $tag->get_attribute(c_base_markup_attributes::ATTRIBUTE_SOURCE_LANGUAGE)->get_value_exact(); if (!empty($attribute)) { - $language_array = c_base_defaults_global::s_get_language()->s_get_aliases_by_id($attribute)->get_value_exact(); + $language_array = c_base_defaults_global::s_get_languages()->s_get_aliases_by_id($attribute)->get_value_exact(); // use the first language alias available. $language = array_pop($language_array); diff --git a/examples/http.php b/examples/http.php index 8c6dd87..824050a 100644 --- a/examples/http.php +++ b/examples/http.php @@ -2,11 +2,16 @@ // make sure the class files can be loaded. set_include_path('.'); + // load the global defaults file (this file is not included by default but is required by all). + // replace this with your own as you see fit. + require_once('common/base/classes/base_defaults_global.php'); + $root_path = 'common/base/classes/'; require_once($root_path . 'base_http.php'); require_once($root_path . 'base_database.php'); require_once($root_path . 'base_cookie.php'); + require_once($root_path . 'base_languages.php'); class_alias('c_base_return', 'c_return'); class_alias('c_base_return_status', 'c_status'); @@ -72,6 +77,9 @@ // only enable output buffering during the output stage, keep it disabled until then. ini_set('output_buffering', FALSE); + + // default supported languages. + c_base_defaults_global::s_set_languages(new c_base_language_limited()); } function program_load_session(&$data_program) { @@ -164,6 +172,7 @@ $data_program['http']->do_load_request(); $data_program['http']->set_response_content(""); + $data_program['http']->set_response_content_language(); program_load_session($data_program); } @@ -299,7 +308,7 @@ } function program_build_response(&$data_program) { - $data_program['http']->set_language_class('c_base_language_us_limited'); + $data_program['http']->set_language_class(c_base_defaults_global::s_get_languages_class()->get_value_exact()); $data_program['http']->set_response_protocol('HTTP/1.1'); $data_program['http']->set_response_allow(c_base_http::HTTP_METHOD_GET); diff --git a/examples/test.php b/examples/test.php index 464b3b3..167c765 100755 --- a/examples/test.php +++ b/examples/test.php @@ -13,6 +13,7 @@ require_once('common/base/classes/base_database.php'); require_once('common/base/classes/base_ldap.php'); require_once('common/base/classes/base_http.php'); + require_once('common/base/classes/base_languages.php'); // create an alias for the default language for error messages. @@ -20,16 +21,24 @@ function process_received_headers(&$stuff) { + // default supported languages. + c_base_defaults_global::s_set_languages(new c_base_language_limited()); + $stuff['http'] = new c_base_http(); $stuff['http']->do_load_request(); + $stuff['http']->set_response_content_language(); // test error message handling using english or japanese. $supported_languages = array( + i_base_language::ENGLISH_US => 'c_base_error_messages_english', i_base_language::ENGLISH => 'c_base_error_messages_english', i_base_language::JAPANESE => 'c_base_error_messages_japanese', ); - $language_chosen = i_base_language::ENGLISH; + $stuff['http']->set_response_content_language(i_base_language::ENGLISH_US); + $stuff['http']->set_response_content_language(i_base_language::ENGLISH); + + $language_chosen = i_base_language::ENGLISH_US; $languages_accepted = $stuff['http']->get_request(c_base_http::REQUEST_ACCEPT_LANGUAGE)->get_value(); if (isset($languages_accepted['data']['weight']) && is_array($languages_accepted['data']['weight'])) { foreach ($languages_accepted['data']['weight'] as $weight => $language) { @@ -48,11 +57,12 @@ } unset($languages_accepted); - if ($language_chosen === i_base_language::ENGLISH) { + if ($language_chosen === i_base_language::ENGLISH || $language_chosen === i_base_language::ENGLISH_US) { require_once('common/base/classes/base_error_messages_english.php'); } elseif ($language_chosen === i_base_language::JAPANESE) { require_once('common/base/classes/base_error_messages_japanese.php'); + $stuff['http']->set_response_content_language(i_base_language::JAPANESE); } $stuff['error_messages'] = new $supported_languages[$language_chosen]; @@ -68,6 +78,28 @@ ); function send_prepared_headers($stuff) { + // add headers + $stuff['http']->set_response_date(); + $stuff['http']->set_response_content_type('text/html'); + #$stuff['http']->set_response_etag(); + #$stuff['http']->set_response_last_modified(strtotime('now')); + #$stuff['http']->set_response_expires(strtotime('+30 minutes')); + $stuff['http']->set_response_pragma('no-cache'); + $stuff['http']->set_response_vary('Host'); + $stuff['http']->set_response_vary('User-Agent'); + $stuff['http']->set_response_vary('Accept'); + $stuff['http']->set_response_vary('Accept-Language'); + #$stuff['http']->set_response_warning('1234 This site is under active development.'); + + + // manually disable output buffering (if enabled) when transfer headers and content. + $old_output_buffering = ini_get('output_buffering'); + ini_set('output_buffering', 'off'); + + + // when the headers are sent, checksums are created, so at this point all error output should be stored and not sent. + $stuff['http']->send_response_headers(TRUE); + if (isset($stuff['cookie_existence']['cookie'])) { $stuff['cookie_existence']['cookie']->do_push(); } @@ -140,6 +172,7 @@ // disclaimer: I used translate.google.com to generate the languages and provided only the default translation (expect translation errors). $test_strings = array( + i_base_language::ENGLISH_US => 'This is a test using your browser default language. Currently english (default), spanish, japanese, and russian are tested.', i_base_language::ENGLISH => 'This is a test using your browser default language. Currently english (default), spanish, japanese, and russian are tested.', i_base_language::JAPANESE => 'これは、ブラウザのデフォルト言語を使用したテストです。 現在、英語(デフォルト)、スペイン語、日本語、ロシア語がテストされています。', i_base_language::RUSSIAN => 'Это тест с помощью браузера по умолчанию язык. В настоящее время английский (по умолчанию), испанский, японский и русский тестируются.', diff --git a/program/reservation/index.php b/program/reservation/index.php index 7ebab2c..33b6904 100644 --- a/program/reservation/index.php +++ b/program/reservation/index.php @@ -13,6 +13,7 @@ require_once('common/base/classes/base_html.php'); require_once('common/base/classes/base_charset.php'); require_once('common/base/classes/base_database.php'); + require_once('common/base/classes/base_languages.php'); require_once('common/theme/classes/theme_html.php'); @@ -64,6 +65,9 @@ $settings['ldap_base_dn'] = 'ou=users,ou=People'; $settings['ldap_fields'] = array('mail', 'gecos', 'givenname', 'cn', 'sn', 'employeenumber'); + // default supported languages. + c_base_defaults_global::s_set_languages(new c_base_language_limited()); + return $settings; } -- 1.8.3.1