diff --git a/application/Config/DocTypes.php b/application/Config/DocTypes.php new file mode 100755 index 000000000000..37830984c31f --- /dev/null +++ b/application/Config/DocTypes.php @@ -0,0 +1,33 @@ + '', + 'xhtml1-strict' => '', + 'xhtml1-trans' => '', + 'xhtml1-frame' => '', + 'xhtml-basic11' => '', + 'html5' => '', + 'html4-strict' => '', + 'html4-trans' => '', + 'html4-frame' => '', + 'mathml1' => '', + 'mathml2' => '', + 'svg10' => '', + 'svg11' => '', + 'svg11-basic' => '', + 'svg11-tiny' => '', + 'xhtml-math-svg-xh' => '', + 'xhtml-math-svg-sh' => '', + 'xhtml-rdfa-1' => '', + 'xhtml-rdfa-2' => '' + ]; +} \ No newline at end of file diff --git a/system/Common.php b/system/Common.php index 06524a4db88d..23a1b3dafbae 100644 --- a/system/Common.php +++ b/system/Common.php @@ -26,12 +26,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * - * @package CodeIgniter - * @author CodeIgniter Dev Team - * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) - * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com - * @since Version 3.0.0 + * @package CodeIgniter + * @author CodeIgniter Dev Team + * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) + * @license http://opensource.org/licenses/MIT MIT License + * @link http://codeigniter.com + * @since Version 3.0.0 * @filesource */ @@ -55,277 +55,277 @@ if (! function_exists('cache')) { - /** - * A convenience method that provides access to the Cache - * object. If no parameter is provided, will return the object, - * otherwise, will attempt to return the cached value. - * - * Examples: - * cache()->save('foo', 'bar'); - * $foo = cache('bar'); - * - * @param string|null $key - * - * @return mixed - */ - function cache(string $key = null) - { - $cache = \Config\Services::cache(); - - // No params - return cache object - if (is_null($key)) - { - return $cache; - } - - // Still here? Retrieve the value. - return $cache->get($key); - } + /** + * A convenience method that provides access to the Cache + * object. If no parameter is provided, will return the object, + * otherwise, will attempt to return the cached value. + * + * Examples: + * cache()->save('foo', 'bar'); + * $foo = cache('bar'); + * + * @param string|null $key + * + * @return mixed + */ + function cache(string $key = null) + { + $cache = \Config\Services::cache(); + + // No params - return cache object + if (is_null($key)) + { + return $cache; + } + + // Still here? Retrieve the value. + return $cache->get($key); + } } //-------------------------------------------------------------------- if ( ! function_exists('view')) { - /** - * Grabs the current RendererInterface-compatible class - * and tells it to render the specified view. Simply provides - * a convenience method that can be used in Controllers, - * libraries, and routed closures. - * - * NOTE: Does not provide any escaping of the data, so that must - * all be handled manually by the developer. - * - * @param string $name - * @param array $data - * @param array $options Unused - reserved for third-party extensions. - * - * @return string - */ - function view(string $name, array $data = [], array $options = []) - { - /** - * @var CodeIgniter\View\View $renderer - */ - $renderer = Services::renderer(); - - $saveData = null; - if (array_key_exists('saveData', $options) && $options['saveData'] === true) - { - $saveData = (bool)$options['saveData']; - unset($options['saveData']); - } - - return $renderer->setData($data, 'raw') - ->render($name, $options, $saveData); - } + /** + * Grabs the current RendererInterface-compatible class + * and tells it to render the specified view. Simply provides + * a convenience method that can be used in Controllers, + * libraries, and routed closures. + * + * NOTE: Does not provide any escaping of the data, so that must + * all be handled manually by the developer. + * + * @param string $name + * @param array $data + * @param array $options Unused - reserved for third-party extensions. + * + * @return string + */ + function view(string $name, array $data = [], array $options = []) + { + /** + * @var CodeIgniter\View\View $renderer + */ + $renderer = Services::renderer(); + + $saveData = null; + if (array_key_exists('saveData', $options) && $options['saveData'] === true) + { + $saveData = (bool)$options['saveData']; + unset($options['saveData']); + } + + return $renderer->setData($data, 'raw') + ->render($name, $options, $saveData); + } } //-------------------------------------------------------------------- if (! function_exists('view_cell')) { - /** - * View cells are used within views to insert HTML chunks that are managed - * by other classes. - * - * @param string $library - * @param null $params - * @param int $ttl - * @param string|null $cacheName - * - * @return string - */ - function view_cell(string $library, $params = null, int $ttl = 0, string $cacheName = null) - { - return Services::viewcell()->render($library, $params, $ttl, $cacheName); - } + /** + * View cells are used within views to insert HTML chunks that are managed + * by other classes. + * + * @param string $library + * @param null $params + * @param int $ttl + * @param string|null $cacheName + * + * @return string + */ + function view_cell(string $library, $params = null, int $ttl = 0, string $cacheName = null) + { + return Services::viewcell()->render($library, $params, $ttl, $cacheName); + } } //-------------------------------------------------------------------- if ( ! function_exists('esc')) { - /** - * Performs simple auto-escaping of data for security reasons. - * Might consider making this more complex at a later date. - * - * If $data is a string, then it simply escapes and returns it. - * If $data is an array, then it loops over it, escaping each - * 'value' of the key/value pairs. - * - * Valid context values: html, js, css, url, attr, raw, null - * - * @param string|array $data - * @param string $context - * @param string $encoding - * - * @return $data - */ - function esc($data, $context = 'html', $encoding=null) - { - if (is_array($data)) - { - foreach ($data as $key => &$value) - { - $value = esc($value, $context); - } - } - - if (is_string($data)) - { - $context = strtolower($context); - - // Provide a way to NOT escape data since - // this could be called automatically by - // the View library. - if (empty($context) || $context == 'raw') - { - return $data; - } - - if ( ! in_array($context, ['html', 'js', 'css', 'url', 'attr'])) - { - throw new \InvalidArgumentException('Invalid escape context provided.'); - } - - if ($context == 'attr') - { - $method = 'escapeHtmlAttr'; - } - else - { - $method = 'escape'.ucfirst($context); - } - - // @todo Optimize this to only load a single instance during page request. - $escaper = new \Zend\Escaper\Escaper($encoding); - - $data = $escaper->$method($data); - } - - return $data; - } + /** + * Performs simple auto-escaping of data for security reasons. + * Might consider making this more complex at a later date. + * + * If $data is a string, then it simply escapes and returns it. + * If $data is an array, then it loops over it, escaping each + * 'value' of the key/value pairs. + * + * Valid context values: html, js, css, url, attr, raw, null + * + * @param string|array $data + * @param string $context + * @param string $encoding + * + * @return $data + */ + function esc($data, $context = 'html', $encoding=null) + { + if (is_array($data)) + { + foreach ($data as $key => &$value) + { + $value = esc($value, $context); + } + } + + if (is_string($data)) + { + $context = strtolower($context); + + // Provide a way to NOT escape data since + // this could be called automatically by + // the View library. + if (empty($context) || $context == 'raw') + { + return $data; + } + + if ( ! in_array($context, ['html', 'js', 'css', 'url', 'attr'])) + { + throw new \InvalidArgumentException('Invalid escape context provided.'); + } + + if ($context == 'attr') + { + $method = 'escapeHtmlAttr'; + } + else + { + $method = 'escape'.ucfirst($context); + } + + // @todo Optimize this to only load a single instance during page request. + $escaper = new \Zend\Escaper\Escaper($encoding); + + $data = $escaper->$method($data); + } + + return $data; + } } //-------------------------------------------------------------------- if (! function_exists('session')) { - /** - * A convenience method for accessing the session instance, - * or an item that has been set in the session. - * - * Examples: - * session()->set('foo', 'bar'); - * $foo = session('bar'); - * - * @param null $val - * - * @return \CodeIgniter\Session\Session|null|void - */ - function session($val = null) - { - // Returning a single item? - if (is_string($val)) - { - return $_SESSION[$val] ?? null; - } - - return \Config\Services::session(); - } + /** + * A convenience method for accessing the session instance, + * or an item that has been set in the session. + * + * Examples: + * session()->set('foo', 'bar'); + * $foo = session('bar'); + * + * @param null $val + * + * @return \CodeIgniter\Session\Session|null|void + */ + function session($val = null) + { + // Returning a single item? + if (is_string($val)) + { + return $_SESSION[$val] ?? null; + } + + return \Config\Services::session(); + } } //-------------------------------------------------------------------- if (! function_exists('timer')) { - /** - * A convenience method for working with the timer. - * If no parameter is passed, it will return the timer instance, - * otherwise will start or stop the timer intelligently. - * - * @param string|null $name - * - * @return $this|\CodeIgniter\Debug\Timer|mixed - */ - function timer(string $name = null) - { - $timer = \Config\Services::timer(); - - if (empty($name)) - { - return $timer; - } - - if ($timer->has($name)) - { - return $timer->stop($name); - } - - return $timer->start($name); - } + /** + * A convenience method for working with the timer. + * If no parameter is passed, it will return the timer instance, + * otherwise will start or stop the timer intelligently. + * + * @param string|null $name + * + * @return $this|\CodeIgniter\Debug\Timer|mixed + */ + function timer(string $name = null) + { + $timer = \Config\Services::timer(); + + if (empty($name)) + { + return $timer; + } + + if ($timer->has($name)) + { + return $timer->stop($name); + } + + return $timer->start($name); + } } //-------------------------------------------------------------------- if (! function_exists('service')) { - /** - * Allows cleaner access to the Services Config file. - * - * These are equal: - * - $timer = service('timer') - * - $timer = \CodeIgniter\Services::timer(); - * - * @param string $name - * @param ...$params - * - * @return mixed - */ - function service(string $name, ...$params) - { - // Ensure it's not a shared instance - array_push($params, false); - - return Services::$name(...$params); - } + /** + * Allows cleaner access to the Services Config file. + * + * These are equal: + * - $timer = service('timer') + * - $timer = \CodeIgniter\Services::timer(); + * + * @param string $name + * @param ...$params + * + * @return mixed + */ + function service(string $name, ...$params) + { + // Ensure it's not a shared instance + array_push($params, false); + + return Services::$name(...$params); + } } //-------------------------------------------------------------------- if (! function_exists('shared_service')) { - /** - * Allow cleaner access to shared services - * - * @param string $name - * @param array|null $params - * @return type - */ - function shared_service(string $name, ...$params) - { - return Services::$name(...$params); - } + /** + * Allow cleaner access to shared services + * + * @param string $name + * @param array|null $params + * @return type + */ + function shared_service(string $name, ...$params) + { + return Services::$name(...$params); + } } //-------------------------------------------------------------------- if (! function_exists('lang')) { - /** - * A convenience method to translate a string and format it - * with the intl extension's MessageFormatter object. - * - * @param string $line - * @param array $args - * - * @return string - */ - function lang(string $line, array $args=[]) - { - return Services::language()->getLine($line, $args); - } + /** + * A convenience method to translate a string and format it + * with the intl extension's MessageFormatter object. + * + * @param string $line + * @param array $args + * + * @return string + */ + function lang(string $line, array $args=[]) + { + return Services::language()->getLine($line, $args); + } } //-------------------------------------------------------------------- @@ -334,40 +334,40 @@ function lang(string $line, array $args=[]) if ( ! function_exists('log_message')) { - /** - * A convenience/compatibility method for logging events through - * the Log system. - * - * Allowed log levels are: - * - emergency - * - alert - * - critical - * - error - * - warning - * - notice - * - info - * - debug - * - * @param string $level - * @param string $message - * @param array|null $context - * - * @return mixed - */ - function log_message(string $level, string $message, array $context = []) - { - // When running tests, we want to always ensure that the - // TestLogger is running, which provides utilities for - // for asserting that logs were called in the test code. - if (ENVIRONMENT == 'testing') - { - $logger = new \CodeIgniter\Log\TestLogger(new \Config\Logger()); - return $logger->log($level, $message, $context); - } - - return Services::logger(true) - ->log($level, $message, $context); - } + /** + * A convenience/compatibility method for logging events through + * the Log system. + * + * Allowed log levels are: + * - emergency + * - alert + * - critical + * - error + * - warning + * - notice + * - info + * - debug + * + * @param string $level + * @param string $message + * @param array|null $context + * + * @return mixed + */ + function log_message(string $level, string $message, array $context = []) + { + // When running tests, we want to always ensure that the + // TestLogger is running, which provides utilities for + // for asserting that logs were called in the test code. + if (ENVIRONMENT == 'testing') + { + $logger = new \CodeIgniter\Log\TestLogger(new \Config\Logger()); + return $logger->log($level, $message, $context); + } + + return Services::logger(true) + ->log($level, $message, $context); + } } //-------------------------------------------------------------------- @@ -375,97 +375,97 @@ function log_message(string $level, string $message, array $context = []) if ( ! function_exists('is_cli')) { - /** - * Is CLI? - * - * Test to see if a request was made from the command line. - * - * @return bool - */ - function is_cli() - { - return (PHP_SAPI === 'cli' || defined('STDIN')); - } + /** + * Is CLI? + * + * Test to see if a request was made from the command line. + * + * @return bool + */ + function is_cli() + { + return (PHP_SAPI === 'cli' || defined('STDIN')); + } } //-------------------------------------------------------------------- if ( ! function_exists('route_to')) { - /** - * Given a controller/method string and any params, - * will attempt to build the relative URL to the - * matching route. - * - * NOTE: This requires the controller/method to - * have a route defined in the routes Config file. - * - * @param string $method - * @param ...$params - * - * @return \CodeIgniter\Router\string - */ - function route_to(string $method, ...$params): string - { - $routes = Services::routes(); - - return $routes->reverseRoute($method, ...$params); - } + /** + * Given a controller/method string and any params, + * will attempt to build the relative URL to the + * matching route. + * + * NOTE: This requires the controller/method to + * have a route defined in the routes Config file. + * + * @param string $method + * @param ...$params + * + * @return \CodeIgniter\Router\string + */ + function route_to(string $method, ...$params): string + { + $routes = Services::routes(); + + return $routes->reverseRoute($method, ...$params); + } } //-------------------------------------------------------------------- if ( ! function_exists('remove_invisible_characters')) { - /** - * Remove Invisible Characters - * - * This prevents sandwiching null characters - * between ascii characters, like Java\0script. - * - * @param string - * @param bool - * @return string - */ - function remove_invisible_characters($str, $url_encoded = TRUE) - { - $non_displayables = array(); - - // every control character except newline (dec 10), - // carriage return (dec 13) and horizontal tab (dec 09) - if ($url_encoded) - { - $non_displayables[] = '/%0[0-8bcef]/'; // url encoded 00-08, 11, 12, 14, 15 - $non_displayables[] = '/%1[0-9a-f]/'; // url encoded 16-31 - } - - $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127 - - do - { - $str = preg_replace($non_displayables, '', $str, -1, $count); - } - while ($count); - - return $str; - } + /** + * Remove Invisible Characters + * + * This prevents sandwiching null characters + * between ascii characters, like Java\0script. + * + * @param string + * @param bool + * @return string + */ + function remove_invisible_characters($str, $url_encoded = TRUE) + { + $non_displayables = array(); + + // every control character except newline (dec 10), + // carriage return (dec 13) and horizontal tab (dec 09) + if ($url_encoded) + { + $non_displayables[] = '/%0[0-8bcef]/'; // url encoded 00-08, 11, 12, 14, 15 + $non_displayables[] = '/%1[0-9a-f]/'; // url encoded 16-31 + } + + $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127 + + do + { + $str = preg_replace($non_displayables, '', $str, -1, $count); + } + while ($count); + + return $str; + } } //-------------------------------------------------------------------- if (! function_exists('helper')) { - /** - * Loads a helper file into memory. Supports namespaced helpers, - * both in and out of the 'helpers' directory of a namespaced directory. - * - * @param string|array $filenames - * - * @return string - */ - function helper($filenames)//: string - { - $loader = Services::locator(true); + /** + * Loads a helper file into memory. Supports namespaced helpers, + * both in and out of the 'helpers' directory of a namespaced directory. + * + * @param string|array $filenames + * + * @return string + */ + function helper($filenames)//: string + { + $loader = Services::locator(true); if (! is_array($filenames)) { @@ -486,63 +486,63 @@ function helper($filenames)//: string include $path; } } - } + } } //-------------------------------------------------------------------- if (! function_exists('app_timezone')) { - /** - * Returns the timezone the application has been set to display - * dates in. This might be different than the timezone set - * at the server level, as you often want to stores dates in UTC - * and convert them on the fly for the user. - */ - function app_timezone() - { - $config = new \Config\App(); - - return $config->appTimezone; - } + /** + * Returns the timezone the application has been set to display + * dates in. This might be different than the timezone set + * at the server level, as you often want to stores dates in UTC + * and convert them on the fly for the user. + */ + function app_timezone() + { + $config = new \Config\App(); + + return $config->appTimezone; + } } //-------------------------------------------------------------------- if (! function_exists('csrf_token')) { - /** - * Returns the CSRF token name. - * Can be used in Views when building hidden inputs manually, - * or used in javascript vars when using APIs. - * - * @return string - */ - function csrf_token() - { - $config = new \Config\App(); - - return $config->CSRFTokenName; - } + /** + * Returns the CSRF token name. + * Can be used in Views when building hidden inputs manually, + * or used in javascript vars when using APIs. + * + * @return string + */ + function csrf_token() + { + $config = new \Config\App(); + + return $config->CSRFTokenName; + } } //-------------------------------------------------------------------- if (! function_exists('csrf_hash')) { - /** - * Returns the current hash value for the CSRF protection. - * Can be used in Views when building hidden inputs manually, - * or used in javascript vars for API usage. - * - * @return string - */ - function csrf_hash() - { - $security = Services::security(null, true); - - return $security->getCSRFHash(); - } + /** + * Returns the current hash value for the CSRF protection. + * Can be used in Views when building hidden inputs manually, + * or used in javascript vars for API usage. + * + * @return string + */ + function csrf_hash() + { + $security = Services::security(null, true); + + return $security->getCSRFHash(); + } } //-------------------------------------------------------------------- @@ -564,123 +564,123 @@ function csrf_field() if (! function_exists('force_https')) { - /** - * Used to force a page to be accessed in via HTTPS. - * Uses a standard redirect, plus will set the HSTS header - * for modern browsers that support, which gives best - * protection against man-in-the-middle attacks. - * - * @see https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security - * - * @param int $duration How long should the SSL header be set for? (in seconds) - * Defaults to 1 year. - * @param RequestInterface $request - * @param ResponseInterface $response - */ - function force_https(int $duration = 31536000, RequestInterface $request = null, ResponseInterface $response = null) - { - if (is_null($request)) $request = Services::request(null, true); - if (is_null($response)) $response = Services::response(null, true); - - if ($request->isSecure()) - { - return; - } - - // If the session library is loaded, we should regenerate - // the session ID for safety sake. - if (class_exists('Session', false)) - { - Services::session(null, true)->regenerate(); - } - - $uri = $request->uri; - $uri->setScheme('https'); - - $uri = \CodeIgniter\HTTP\URI::createURIString( - $uri->getScheme(), - $uri->getAuthority(true), - $uri->getPath(), // Absolute URIs should use a "/" for an empty path - $uri->getQuery(), - $uri->getFragment() - ); - - // Set an HSTS header - $response->setHeader('Strict-Transport-Security', 'max-age='.$duration); - $response->redirect($uri); - exit(); - } + /** + * Used to force a page to be accessed in via HTTPS. + * Uses a standard redirect, plus will set the HSTS header + * for modern browsers that support, which gives best + * protection against man-in-the-middle attacks. + * + * @see https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security + * + * @param int $duration How long should the SSL header be set for? (in seconds) + * Defaults to 1 year. + * @param RequestInterface $request + * @param ResponseInterface $response + */ + function force_https(int $duration = 31536000, RequestInterface $request = null, ResponseInterface $response = null) + { + if (is_null($request)) $request = Services::request(null, true); + if (is_null($response)) $response = Services::response(null, true); + + if ($request->isSecure()) + { + return; + } + + // If the session library is loaded, we should regenerate + // the session ID for safety sake. + if (class_exists('Session', false)) + { + Services::session(null, true)->regenerate(); + } + + $uri = $request->uri; + $uri->setScheme('https'); + + $uri = \CodeIgniter\HTTP\URI::createURIString( + $uri->getScheme(), + $uri->getAuthority(true), + $uri->getPath(), // Absolute URIs should use a "/" for an empty path + $uri->getQuery(), + $uri->getFragment() + ); + + // Set an HSTS header + $response->setHeader('Strict-Transport-Security', 'max-age='.$duration); + $response->redirect($uri); + exit(); + } } //-------------------------------------------------------------------- if (! function_exists('redirect')) { - /** - * Convenience method that works with the current global $request and - * $router instances to redirect using named/reverse-routed routes - * to determine the URL to go to. If nothing is found, will treat - * as a traditional redirect and pass the string in, letting - * $response->redirect() determine the correct method and code. - * - * If more control is needed, you must use $response->redirect explicitly. - * - * @param string $uri - * @param $params - */ - function redirect(string $uri, ...$params) - { - $response = Services::response(null, true); - $routes = Services::routes(true); - - if ($route = $routes->reverseRoute($uri, ...$params)) - { - $uri = $route; - } - - $response->redirect($uri); - } + /** + * Convenience method that works with the current global $request and + * $router instances to redirect using named/reverse-routed routes + * to determine the URL to go to. If nothing is found, will treat + * as a traditional redirect and pass the string in, letting + * $response->redirect() determine the correct method and code. + * + * If more control is needed, you must use $response->redirect explicitly. + * + * @param string $uri + * @param $params + */ + function redirect(string $uri, ...$params) + { + $response = Services::response(null, true); + $routes = Services::routes(true); + + if ($route = $routes->reverseRoute($uri, ...$params)) + { + $uri = $route; + } + + $response->redirect($uri); + } } //-------------------------------------------------------------------- if ( ! function_exists('stringify_attributes')) { - /** - * Stringify attributes for use in HTML tags. - * - * Helper function used to convert a string, array, or object - * of attributes to a string. - * - * @param mixed string, array, object - * @param bool - * @return string - */ - function stringify_attributes($attributes, $js = FALSE) : string - { - $atts = ''; - - if (empty($attributes)) - { - return $atts; - } - - if (is_string($attributes)) - { - return ' '.$attributes; - } - - $attributes = (array) $attributes; - - foreach ($attributes as $key => $val) - { - $atts .= ($js) + /** + * Stringify attributes for use in HTML tags. + * + * Helper function used to convert a string, array, or object + * of attributes to a string. + * + * @param mixed string, array, object + * @param bool + * @return string + */ + function stringify_attributes($attributes, $js = FALSE) : string + { + $atts = ''; + + if (empty($attributes)) + { + return $atts; + } + + if (is_string($attributes)) + { + return ' '.$attributes; + } + + $attributes = (array) $attributes; + + foreach ($attributes as $key => $val) + { + $atts .= ($js) ? $key.'='.esc($val, 'js').',' : ' '.$key.'="'.esc($val, 'attr').'"'; - } + } - return rtrim($atts, ','); - } + return rtrim($atts, ','); + } } //-------------------------------------------------------------------- @@ -694,9 +694,9 @@ function stringify_attributes($attributes, $js = FALSE) : string * the file, based on the read-only attribute. is_writable() is also unreliable * on Unix servers if safe_mode is on. * - * @link https://bugs.php.net/bug.php?id=54709 - * @param string - * @return bool + * @link https://bugs.php.net/bug.php?id=54709 + * @param string + * @return bool */ function is_really_writable($file) { @@ -733,3 +733,29 @@ function is_really_writable($file) } //-------------------------------------------------------------------- + +if ( ! function_exists('slash_item')) +{ + //Unlike CI3, this function is placed here because + //it's not a config, or part of a config. + /** + * Fetch a config file item with slash appended (if not empty) + * + * @param string $item Config item name + * @return string|null The configuration item or NULL if + * the item doesn't exist + */ + function slash_item($item) + { + $config = new \Config\App(); + $configItem = $config->{$item}; + + if ( ! isset($configItem) || empty(trim($configItem))) + { + return $configItem; + } + + return rtrim($configItem, '/') . '/'; + } +} +//-------------------------------------------------------------------- diff --git a/system/Helpers/html_helper.php b/system/Helpers/html_helper.php new file mode 100755 index 000000000000..8d1404f5af15 --- /dev/null +++ b/system/Helpers/html_helper.php @@ -0,0 +1,845 @@ +\n"; + + + // Cycle through the list elements. If an array is + // encountered we will recursively call _list() + + static $_last_list_item = ''; + foreach ($list as $key => $val) + { + $_last_list_item = $key; + + $out .= str_repeat(' ', $depth + 2) . '
  • '; + + if ( ! is_array($val)) + { + $out .= $val; + } + else + { + $out .= $_last_list_item + . "\n" + . _list($type, $val, '', $depth + 4) + . str_repeat(' ', $depth + 2); + } + + $out .= "
  • \n"; + } + + // Set the indentation for the closing tag and apply it + return $out . str_repeat(' ', $depth) . '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('img')) +{ + /** + * Image + * + * Generates an image element + * + * @param mixed $src + * @param bool $indexPage + * @param string $attributes + * @return string + */ + function img + ( + $src = '', + bool $indexPage = false, + string $attributes = '' + ): string + { + if ( ! is_array($src) ) + { + $src = ['src' => $src]; + } + + + //If there is no alt attribute defined, set it to an empty string. + if ( ! isset($src['alt'])) + { + $src['alt'] = ''; + } + + $img = ' $v) + { + //Include a protocol if nothing is explicitely defined. + if ($k === 'src' && ! preg_match('#^([a-z]+:)?//#i', $v)) + { + if ($indexPage === true) + { + $img .= ' src="' . site_url($v) . '"'; + } + else + { + $img .= ' src="' . slash_item('baseURL') . $v . '"'; + } + } + else + { + $img .= ' ' . $k . '="' . $v . '"'; + } + } + + return $img . stringify_attributes($attributes) . ' />'; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('doctype')) +{ + /** + * Doctype + * + * Generates a page document type declaration + * + * Examples of valid options: html5, xhtml-11, xhtml-strict, xhtml-trans, + * xhtml-frame, html4-strict, html4-trans, and html4-frame. + * All values are saved in the doctypes config file. + * + * @param mixed $type The doctype to be generated + * @return string + */ + function doctype($type = 'html5'): string + { + $doctypes = null; + $env = ENVIRONMENT; + $doctypes = Config\DocTypes::$list; + $customDocTypesPath = APPPATH . "Config/{$env}/DocTypes.php"; + if (file_exists($customDocTypesPath)) + { + $customDocTypesNs = "Config\{$env}\DocTypes"; + $doctypes = $customDocTypesNs::$list; + } + return isset($doctypes[$type]) ? $doctypes[$type] : false; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('script_tag')) +{ + /** + * Script + * + * Generates link to a JS file + * + * @param mixed $src Script source or an array + * @param bool $indexPage Should indexPage be added to the JS path + * @return string + */ + function script_tag + ( + $src = '', + bool $indexPage = false + ): string + { + $script = '"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('link_tag')) +{ + /** + * Link + * + * Generates link to a CSS file + * + * @param mixed $href Stylesheet hrefs or an array + * @param string $rel + * @param string $type + * @param string $title + * @param string $media + * @param bool $indexPage should indexPage be added to the CSS path. + * @return string + */ + function link_tag + ( + $href = '', + string $rel = 'stylesheet', + string $type = 'text/css', + string $title = '', + string $media = '', + bool $indexPage = false + ): string + { + $link = ' $v) + { + if ($k === 'href' && ! preg_match('#^([a-z]+:)?//#i', $v)) + { + if ($indexPage === true) + { + $link .= 'href="' . site_url($v) . '" '; + } + else + { + $link .= 'href="' . slash_item('baseURL') . $v . '" '; + } + } + else + { + $link .= $k . '="' . $v . '" '; + } + } + } + else + { + if (preg_match('#^([a-z]+:)?//#i', $href)) + { + $link .= 'href="' . $href . '" '; + } + elseif ($indexPage === true) + { + $link .= 'href="' . site_url($href) . '" '; + } + else + { + $link .= 'href="' . slash_item('baseURL') . $href . '" '; + } + + $link .= 'rel="' . $rel . '" type="' . $type . '" '; + + if ($media !== '') + { + $link .= 'media="' . $media . '" '; + } + + if ($title !== '') + { + $link .= 'title="' . $title . '" '; + } + } + + return $link . "/>"; + } + + // ------------------------------------------------------------------------ + + if ( ! function_exists('video')) + { + /** + * Video + * + * Geneartes a video element to embed videos. The video element can + * contain one or more video sources + * + * @param mixed $src Either a source string or + * an array of sources + * @param string $unsupportedMessage The message to display + * if the media tag is not supported by the browser + * @param string $attributes HTML attributes + * @param array $tracks + * @param bool $indexPage + * @return string + * + */ + function video + ( + $src, + string $unsupportedMessage = '', + string $attributes = '', + array $tracks = [], + bool $indexPage = false + ): string + { + if(is_array($src)) + { + return _media + ( + 'video', + $src, + $unsupportedMessage, + $attributes, + $tracks + ); + } + else + { + $video = ''; + } + + $media .= "\n"; + + foreach($types as $option) + { + $media .= _space_indent() .$option . "\n"; + } + + if(empty($tracks)) + { + $media .= "/>"; + } + else + { + foreach ($tracks as $track) + { + $media .= _space_indent() . $track . "\n"; + } + } + + if(! empty($unsupportedMessage)) + { + $media .= _space_indent() . $unsupportedMessage . "\n"; + } + + $media .= '\n"; + + return $media; + } + } + + // ------------------------------------------------------------------------ + + if ( ! function_exists('source')) + { + /** + * Source + * + * Generates a source element that specifies multiple media resources + * for either audio or video element + * + * @param string $src The path of the media resource + * @param string $type The MIME-type of the resource with + * optional codecs parameters + * @param string $attributes HTML attributes + * @param bool $indexPage + * @return string + */ + function source + ( + string $src, + string $type, + string $attributes = '', + bool $indexPage = false + ): string + { + if(_has_protocol($src)) + { + //Do nothing. + } + elseif ($indexPage === true) + { + $src = site_url($src); + } + else + { + $src = slash_item('baseURL') . $src; + } + + $source = ''; + } + } + + // ------------------------------------------------------------------------ + + if ( ! function_exists('object')) + { + /** + * Object + * + * Generates an object element that represents the media + * as either image or a resource plugin such as audio, video, + * Java applets, ActiveX, PDF and Flash + * + * @param string $data A resource URL + * @param string $type Content-type of the resource + * @param string $attributes HTML attributes + * @param array $params + * @return string + */ + function object + ( + string $data, + string $type, + string $attributes = '', + array $params = [], + bool $indexPage = false + ): string + { + if(_has_protocol($data)) + { + //Do nothing. + } + elseif ($indexPage === true) + { + $data = site_url($data); + } + else + { + $data = slash_item('baseURL') . $data; + } + + $object = ''; + + if( ! empty($params)) + { + $object .= "\n"; + } + + foreach ($params as $param) + { + $object .= _space_indent() . $param . "\n"; + } + + $object .= "\n"; + + return $object; + } + } + + // ------------------------------------------------------------------------ + + if ( ! function_exists('param')) + { + /** + * Param + * + * Generates a param element that defines parameters + * for the object element. + * + * @param string $name The name of the parameter + * @param string $value The value of the parameter + * @param string $type The MIME-type + * @param string $attributes HTML attributes + * @return string + */ + function param + ( + string $name, + string $value, + string $type = 'ref', + string $attributes = '' + ): string + { + return ''; + } + } + + // ------------------------------------------------------------------------ + + if ( ! function_exists('embed')) + { + /** + * Embed + * + * Generates an embed element + * + * @param string $src The path of the resource to embed + * @param string $type MIME-type + * @param string $attributes HTML attributes + * @param bool $indexPage + * @return string + */ + function embed + ( + string $src, + string $type, + string $attributes = '', + bool $indexPage = false + ): string + { + if(_has_protocol($src)) + { + //Do nothing. + } + elseif ($indexPage === true) + { + $src = site_url($src); + } + else + { + $src = slash_item('baseURL') . $src; + } + + return '\n"; + } + } + + // ------------------------------------------------------------------------ + + if ( ! function_exists('_has_protocol')) + { + function _has_protocol($url) + { + return preg_match('#^([a-z]+:)?//#i', $url); + } + } + + // ------------------------------------------------------------------------ + + if ( ! function_exists('_space_indent')) + { + function _space_indent($depth = 2) + { + return str_repeat(' ', $depth); + } + } +} + +// ------------------------------------------------------------------------ \ No newline at end of file diff --git a/tests/system/Helpers/HTMLHelperTest.php b/tests/system/Helpers/HTMLHelperTest.php new file mode 100755 index 000000000000..fb40c62fa8e4 --- /dev/null +++ b/tests/system/Helpers/HTMLHelperTest.php @@ -0,0 +1,247 @@ +tracks = + [ + track('subtitles_no.vtt', 'subtitles', 'no', 'Norwegian No'), + track('subtitles_yes.vtt', 'subtitles', 'yes', 'Norwegian Yes') + ]; + } + + //-------------------------------------------------------------------- + + public function testUL() + { + $expected = << +
  • foo
  • +
  • bar
  • + + +EOH; + + $expected = ltrim($expected); + $list = array('foo', 'bar'); + + $this->assertEquals(ltrim($expected), ul($list)); + + $expected = << +
  • foo
  • +
  • bar
  • + + +EOH; + + $expected = ltrim($expected); + $this->assertEquals($expected, ul($list, 'class="test"')); + } + + // ------------------------------------------------------------------------ + + public function testIMG() + { + //TODO: Mock baseURL and siteURL. + $this->assertEquals + ( + '', + img('http://site.com/images/picture.jpg') + ); + } + + // ------------------------------------------------------------------------ + + public function testScriptTag() + { + $this->assertEquals + ( + '', + script_tag('http://site.com/js/mystyles.js') + ); + } + + // ------------------------------------------------------------------------ + + public function testLinkTag() + { + $this->assertEquals + ( + '', + link_tag('http://site.com/css/mystyles.css') + ); + } + + // ------------------------------------------------------------------------ + + public function testDocType() + { + $this->assertEquals + ( + '', + doctype('html4-strict') + ); + } + + // ------------------------------------------------------------------------ + + public function testVideo() + { + $expected = << + Your browser does not support the video tag. + + +EOH; + + $video = video + ( + 'test.mp4', + 'Your browser does not support the video tag.', + 'controls' + ); + + $this->assertEquals($expected, $video); + + $expected = << + + + Your browser does not support the video tag. + + +EOH; + + $video = video + ( + 'http://www.codeigniter.com/test.mp4', + 'Your browser does not support the video tag.', + 'controls', + $this->tracks + ); + $this->assertEquals($expected, $video); + + $expected = << + + + + + + + Your browser does not support the video tag. + + +EOH; + + $video = video + ( + [ + source('movie.mp4', 'video/mp4', 'class="test"'), + source('movie.ogg', 'video/ogg'), + source('movie.mov', 'video/quicktime'), + source('movie.ogv', 'video/ogv; codecs=dirac, speex') + ], + 'Your browser does not support the video tag.', + 'class="test" controls', + $this->tracks + ); + + $this->assertEquals($expected, $video); + } + + // ------------------------------------------------------------------------ + + public function testAudio() + { + $expected = << + + + + + Your browser does not support the audio tag. + + +EOH; + + $audio = audio + ( + [ + source('sound.ogg', 'audio/ogg'), + source('sound.mpeg', 'audio/mpeg') + ], + 'Your browser does not support the audio tag.', + 'id="test" controls', + $this->tracks + ); + + $this->assertEquals($expected, $audio); + } + + // ------------------------------------------------------------------------ + + public function testEmbed() + { + $expected = << + +EOH; + + $embed = embed('movie.mov', 'video/quicktime', 'class="test"'); + $this->assertEquals($expected, $embed); + } + + public function testObject() + { + $expected = << + +EOH; + + $object = object + ( + 'movie.swf', + 'application/x-shockwave-flash', + 'class="test"' + ); + + $this->assertEquals($expected, $object); + + $expected = << + + + + +EOH; + + $object = object + ( + 'movie.swf', + 'application/x-shockwave-flash', + 'class="test"', + [ + param('foo', 'bar', 'ref', 'class="test"'), + param('hello', 'world', 'ref', 'class="test"') + ] + ); + $this->assertEquals($expected, $object); + } + + // ------------------------------------------------------------------------ + +} \ No newline at end of file diff --git a/user_guide_src/source/helpers/html_helper.rst b/user_guide_src/source/helpers/html_helper.rst new file mode 100755 index 000000000000..3214731202de --- /dev/null +++ b/user_guide_src/source/helpers/html_helper.rst @@ -0,0 +1,488 @@ +########### +HTML Helper +########### + +The HTML Helper file contains functions that assist in working with +HTML. + +.. contents:: + :local: + +.. raw:: html + +
    + +Loading this Helper +=================== + +This helper is loaded using the following code:: + + helper('html'); + +Available Functions +=================== + +The following functions are available: + + +.. php:function:: img([$src = ''[, $indexPage = false[, $attributes = '']]]) + + :param string $src: Image source data + :param bool $indexPage: Whether to treat $src as a routed URI string + :param array $attributes: HTML attributes + :returns: HTML image tag + :rtype: string + + Lets you create HTML tags. The first parameter contains the + image source. Example:: + + echo img('images/picture.jpg'); + // + + There is an optional second parameter that is a true/false value that + specifics if the *src* should have the page specified by + ``$config['indexPage']`` added to the address it creates. + Presumably, this would be if you were using a media controller:: + + echo img('images/picture.jpg', true); + // + + Additionally, an associative array can be passed to the ``img()`` function + for complete control over all attributes and values. If an *alt* attribute + is not provided, CodeIgniter will generate an empty string. + + Example:: + + $imageProperties = array( + 'src' => 'images/picture.jpg', + 'alt' => 'Me, demonstrating how to eat 4 slices of pizza at one time', + 'class' => 'post_images', + 'width' => '200', + 'height'=> '200', + 'title' => 'That was quite a night', + 'rel' => 'lightbox' + ); + + img($imageProperties); + // Me, demonstrating how to eat 4 slices of pizza at one time + +.. php:function:: link_tag([$href = ''[, $rel = 'stylesheet'[, $type = 'text/css'[, $title = ''[, $media = ''[, $indexPage = false]]]]]]) + + :param string $href: The source of the link file + :param string $rel: Relation type + :param string $type: Type of the related document + :param string $title: Link title + :param string $media: Media type + :param bool $indexPage: Whether to treat $src as a routed URI string + :returns: HTML link tag + :rtype: string + + Lets you create HTML tags. This is useful for stylesheet links, + as well as other links. The parameters are *href*, with optional *rel*, + *type*, *title*, *media* and *indexPage*. + + *indexPage* is a boolean value that specifies if the *href* should have + the page specified by ``$config['indexPage']`` added to the address it creates. + + Example:: + + echo link_tag('css/mystyles.css'); + // + + Further examples:: + + echo link_tag('favicon.ico', 'shortcut icon', 'image/ico'); + // + + echo link_tag('feed', 'alternate', 'application/rss+xml', 'My RSS Feed'); + // + + Additionally, an associative array can be passed to the ``link()`` function + for complete control over all attributes and values:: + + $link = array( + 'href' => 'css/printer.css', + 'rel' => 'stylesheet', + 'type' => 'text/css', + 'media' => 'print' + ); + + echo link_tag($link); + // + +.. php:function:: script_tag([$src = ''[, $indexPage = false]]) + + :param string $src: The source of JavaScript file + :param bool $indexPage: Whether to treat $src as a routed URI string + :returns: HTML script tag + :rtype: string + + Lets you create HTML tags. The parameters is *src*, with optional *indexPage*. + + *indexPage* is a boolean value that specifies if the *src* should have + the page specified by ``$config['indexPage']`` added to the address it creates. + + Example:: + + echo script_tag('js/mystyles.js'); + // + + Further examples:: + + Additionally, an associative array can be passed to the ``script_tag()`` function + for complete control over all attributes and values:: + + $script = array('src' => 'js/printer.js'); + + echo script_tag($script); + // + +.. php:function:: ul($list[, $attributes = '']) + + :param array $list: List entries + :param array $attributes: HTML attributes + :returns: HTML-formatted unordered list + :rtype: string + + Permits you to generate unordered HTML lists from simple or + multi-dimensional arrays. Example:: + + $list = array( + 'red', + 'blue', + 'green', + 'yellow' + ); + + $attributes = array( + 'class' => 'boldlist', + 'id' => 'mylist' + ); + + echo ul($list, $attributes); + + The above code will produce this: + + .. code-block:: html + +
      +
    • red
    • +
    • blue
    • +
    • green
    • +
    • yellow
    • +
    + + Here is a more complex example, using a multi-dimensional array:: + + $attributes = array( + 'class' => 'boldlist', + 'id' => 'mylist' + ); + + $list = array( + 'colors' => array( + 'red', + 'blue', + 'green' + ), + 'shapes' => array( + 'round', + 'square', + 'circles' => array( + 'ellipse', + 'oval', + 'sphere' + ) + ), + 'moods' => array( + 'happy', + 'upset' => array( + 'defeated' => array( + 'dejected', + 'disheartened', + 'depressed' + ), + 'annoyed', + 'cross', + 'angry' + ) + ) + ); + + echo ul($list, $attributes); + + The above code will produce this: + + .. code-block:: html + +
      +
    • colors +
        +
      • red
      • +
      • blue
      • +
      • green
      • +
      +
    • +
    • shapes +
        +
      • round
      • +
      • suare
      • +
      • circles +
          +
        • elipse
        • +
        • oval
        • +
        • sphere
        • +
        +
      • +
      +
    • +
    • moods +
        +
      • happy
      • +
      • upset +
          +
        • defeated +
            +
          • dejected
          • +
          • disheartened
          • +
          • depressed
          • +
          +
        • +
        • annoyed
        • +
        • cross
        • +
        • angry
        • +
        +
      • +
      +
    • +
    + +.. php:function:: ol($list, $attributes = '') + + :param array $list: List entries + :param array $attributes: HTML attributes + :returns: HTML-formatted ordered list + :rtype: string + + Identical to :php:func:`ul()`, only it produces the
      tag for + ordered lists instead of
        . + +.. php:function:: video($src[, $unsupportedMessage = ''[, $attributes = ''[, $tracks = [][, $indexPage = false]]]]) + + :param mixed $src: Either a source string or an array of sources. See :php:func:`source()` function + :param string $unsupportedMessage: The message to display if the media tag is not supported by the browser + :param string $attributes: HTML attributes + :param array $tracks: Use the track function inside an array. See :php:func:`track()` function + :param bool $indexPage: + :returns: HTML-formatted video element + :rtype: string + + Permits you to generate HTML video element from simple or + source arrays. Example:: + + $tracks = + [ + track('subtitles_no.vtt', 'subtitles', 'no', 'Norwegian No'), + track('subtitles_yes.vtt', 'subtitles', 'yes', 'Norwegian Yes') + ]; + + echo video('test.mp4', 'Your browser does not support the video tag.', 'controls'); + + echo video + ( + 'http://www.codeigniter.com/test.mp4', + 'Your browser does not support the video tag.', + 'controls', + $tracks + ); + + echo video + ( + [ + source('movie.mp4', 'video/mp4', 'class="test"'), + source('movie.ogg', 'video/ogg'), + source('movie.mov', 'video/quicktime'), + source('movie.ogv', 'video/ogv; codecs=dirac, speex') + ], + 'Your browser does not support the video tag.', + 'class="test" controls', + $tracks + ); + + The above code will produce this: + + .. code-block:: html + + + + + + + +.. php:function:: audio($src[, $unsupportedMessage = ''[, $attributes = ''[, $tracks = [][, $indexPage = false]]]]) + + :param mixed $src: Either a source string or an array of sources. See :php:func:`source()` function + :param string $unsupportedMessage: The message to display if the media tag is not supported by the browser + :param string $attributes: + :param array $tracks: Use the track function inside an array. See :php:func:`track()` function + :param bool $indexPage: + :returns: HTML-formatted video element + :rtype: string + + Identical to :php:func:`video()`, only it produces the