diff --git a/.gitignore b/.gitignore index c9cda2318..4f41b96f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /nbproject/ /output/ +vendor/ diff --git a/README.md b/README.md index 5e5f55fe2..30334b3ec 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,107 @@ # PHP Framework Benchmark +This project attempts to measure minimum overhead (minimum bootstrap cost) of PHP frameworks in the real world. + +So I think the minimum applications to benchmark should not include: + +* cost of template engine (HTML output) +* cost of database manipulation +* cost of debugging information + +Components like Template engine or ORM/Database libraries are out of scope in this project. + +## Benchmarking Policy + +This is `master` branch. + +* Install a framework according to the official documentation. +* Use the default configuration. + * Don't remove any components/configurations even if they are not used. + * With minimum changes to run this benchmark. +* Set environment production/Turn off debug mode. +* Run optimization which you normally do in your production environment, like Composer's `--optimize-autoloader`. +* Use controller or action class if a framework has the functionality. + +Some people may think using default configuration is not fair. But I think a framework's default configuration is an assertion of what it is. Default configuration is a good starting point to know a framework. And I can't optimize all the frameworks. Some frameworks are optimized, some are not, it is not fair. So I don't remove any components/configurations. + +But if you are interested in benchmarking with optimization (removing components/configurations which are not used), See [optimize](https://github.com/kenjis/php-framework-benchmark/tree/optimize) branch. + +If you find something wrong with my code, please feel free to send Pull Requests. But please note optimizing only for "Hello World" is not acceptable. Building fastest "Hello World" application is not the goal in this project. + ## Results +### Benchmarking Environment + +* CentOS 6.8 64bit (VM; VirtualBox) + * PHP 5.6.30 (Remi RPM) + * Zend OPcache v7.0.6-dev + * Apache 2.2 + ### Hello World Benchmark -|framework |requests per second|peak memory| -|-------------------|------------------:|----------:| -|phalcon-1.3 | 1445.99| 0.50| -|codeigniter-3.0 | 698.69| 0.50| -|yii-2.0 | 376.68| 1.50| -|fuel-1.8-dev | 322.90| 0.75| -|silex-1.2 | 311.63| 0.75| -|bear-1.0 | 296.89| 1.00| -|cake-3.0 | 259.01| 1.00| -|symfony-2.6 | 122.58| 2.00| -|laravel-5.0 | 70.63| 3.00| +These are my benchmarks, not yours. **I encourage you to run on your (production equivalent) environments.** + +(2017/02/14) + +![Benchmark Results Graph](img/php-framework-benchmark-20170214.png) + +|framework |requests per second|relative|peak memory|relative| +|-------------------|------------------:|-------:|----------:|-------:| +|siler-0.6 | 2,069.69| 20.3| 0.25| 1.0| +|kumbia-1.0-dev | 1,753.60| 17.2| 0.29| 1.2| +|staticphp-0.9 | 1,665.28| 16.3| 0.27| 1.1| +|phalcon-2.0 | 1,618.39| 15.9| 0.26| 1.1| +|tipsy-0.10 | 1,376.97| 13.5| 0.32| 1.3| +|fatfree-3.5 | 965.16| 9.5| 0.41| 1.7| +|ci-3.0 | 753.09| 7.4| 0.42| 1.7| +|nofuss-1.2 | 667.24| 6.5| 0.40| 1.6| +|slim-3.0 | 550.43| 5.4| 0.61| 2.5| +|bear-1.0 | 502.52| 4.9| 0.73| 3.0| +|lumen-5.1 | 415.57| 4.1| 0.85| 3.5| +|yii-2.0 | 410.08| 4.0| 1.32| 5.4| +|ze-1.0 | 403.34| 4.0| 0.75| 3.1| +|cygnite-1.3 | 369.12| 3.6| 0.71| 2.9| +|fuel-1.8 | 344.26| 3.4| 0.63| 2.6| +|silex-2.0 | 342.81| 3.4| 0.78| 3.2| +|phpixie-3.2 | 267.24| 2.6| 1.25| 5.1| +|aura-2.0 | 233.54| 2.3| 0.88| 3.6| +|cake-3.2 | 174.91| 1.7| 1.95| 7.9| +|zf-3.0 | 133.87| 1.3| 2.24| 9.1| +|symfony-3.0 | 131.50| 1.3| 2.18| 8.9| +|laravel-5.3 | 101.94| 1.0| 2.83| 11.5| + +Note(1): All the results are run on php with `phalcon.so` and `ice.so`. If you don't load phalcon.so or ice.so, the rps except for Phalcon or Ice probably increase a bit. + +Note(2): This benchmarks are limited by `ab` performance. See [#62](https://github.com/kenjis/php-framework-benchmark/issues/62). ## How to Benchmark -Install source code as . +If you want to benchmark PHP extension frameworks like Phalcon, you need to install the extenstions. + +Install source code as : ~~~ $ git clone https://github.com/kenjis/php-framework-benchmark.git $ cd php-framework-benchmark -$ sh setup.sh +$ bash setup.sh ~~~ -Run benchmarks. +Run benchmarks: ~~~ -$ sh benchmark.sh +$ bash benchmark.sh ~~~ See . -## Kernel Configuration +If you want to benchmark some frameworks: + +~~~ +$ bash setup.sh fatfree-3.5/ slim-3.0/ lumen-5.1/ silex-1.3/ +$ bash benchmark.sh fatfree-3.5/ slim-3.0/ lumen-5.1/ silex-1.3/ +~~~ + +## Linux Kernel Configuration I added below in `/etc/sysctl.conf` @@ -52,19 +119,47 @@ and run `sudo sysctl -p`. If you want to see current configuration, run `sudo sysctl -a`. -## Reference +## Apache Virtual Host Configuration -* [Phalcon](http://phalconphp.com/) -* [CodeIgniter](http://www.codeigniter.com/) -* [Yii](http://www.yiiframework.com/) -* [FuelPHP](http://fuelphp.com/) -* [Silex](http://silex.sensiolabs.org/) -* [BEAR.Sunday](https://bearsunday.github.io/) -* [CakePHP](http://cakephp.org/) -* [Symfony](http://symfony.com/) -* [Laravel](http://laravel.com/) +~~~ + + DocumentRoot /home/vagrant/public + +~~~ -## Related +## References + +* [Aura](http://auraphp.com/) ([@auraphp](https://twitter.com/auraphp)) +* [BEAR.Sunday](https://bearsunday.github.io/) ([@BEARSunday](https://twitter.com/BEARSunday)) +* [CakePHP](http://cakephp.org/) ([@cakephp](https://twitter.com/cakephp)) +* [CodeIgniter](http://www.codeigniter.com/) ([@CodeIgniter](https://twitter.com/CodeIgniter)) +* [Cygnite](http://www.cygniteframework.com/) ([@cygnitephp](https://twitter.com/cygnitephp)) +* [FatFree](http://fatfreeframework.com/) ([@phpfatfree](https://twitter.com/phpfatfree)) +* [FuelPHP](http://fuelphp.com/) ([@fuelphp](https://twitter.com/fuelphp)) +* [Ice](http://www.iceframework.org/) ([@iceframework](https://twitter.com/iceframework)) [PHP extension] + * See https://github.com/kenjis/php-framework-benchmark/pull/17#issuecomment-98244668 +* [KumbiaPHP](https://github.com/KumbiaPHP/KumbiaPHP) ([@KumbiaPHP](https://twitter.com/KumbiaPHP)) + * [Install KumbiaPHP](https://github.com/KumbiaPHP/Documentation/blob/master/en/to-install.md#instalar-kumbiaphp) +* [Laravel](http://laravel.com/) ([@laravelphp](https://twitter.com/laravelphp)) +* [Lumen](http://lumen.laravel.com/) +* [NoFussFramework](http://www.nofussframework.com/) +* [Phalcon](http://phalconphp.com/) ([@phalconphp](https://twitter.com/phalconphp)) [PHP extension] + * [Installation](https://docs.phalconphp.com/en/latest/reference/install.html) +* [PHPixie](http://phpixie.com/) ([@phpixie](https://twitter.com/phpixie)) +* [Radar](https://github.com/radarphp/Radar.Project) +* [Siler](https://github.com/leocavalcante/siler) +* [Silex](http://silex.sensiolabs.org/) +* [Slim](http://www.slimframework.com/) ([@slimphp](https://twitter.com/slimphp)) +* [StaticPHP](https://github.com/gintsmurans/staticphp) +* [Symfony](http://symfony.com/) ([@symfony](https://twitter.com/symfony)) + * [How to Deploy a Symfony Application](http://symfony.com/doc/current/cookbook/deployment/tools.html) +* [Tipsy](http://tipsy.la) +* [Flow-Framework](https://flow.neos.io) ([@neoscms](https://twitter.com/neoscms)) +* [Yii](http://www.yiiframework.com/) ([@yiiframework](https://twitter.com/yiiframework)) +* [zend-expressive](https://github.com/zendframework/zend-expressive) ([@zfdevteam](https://twitter.com/zfdevteam)) +* [Zend Framework](http://framework.zend.com/) ([@zfdevteam](https://twitter.com/zfdevteam)) + +## Other Benchmarks * [PHP ORM Benchmark](https://github.com/kenjis/php-orm-benchmark) * [PHP User Agent Parser Benchmarks](https://github.com/kenjis/user-agent-parser-benchmarks) diff --git a/aura-2.0/.gitignore b/aura-2.0/.gitignore new file mode 100644 index 000000000..3a920d285 --- /dev/null +++ b/aura-2.0/.gitignore @@ -0,0 +1,4 @@ +/composer.lock +/tmp/* +!/tmp/.placeholder +/vendor diff --git a/aura-2.0/.travis.yml b/aura-2.0/.travis.yml new file mode 100644 index 000000000..cf463f166 --- /dev/null +++ b/aura-2.0/.travis.yml @@ -0,0 +1,11 @@ +language: php +php: + - 5.4 + - 5.5 + - 5.6 + - 7 +before_script: + - composer self-update + - composer install +script: + - ./phpunit.sh diff --git a/aura-2.0/CHANGES.md b/aura-2.0/CHANGES.md new file mode 100644 index 000000000..8e134ecd7 --- /dev/null +++ b/aura-2.0/CHANGES.md @@ -0,0 +1 @@ +This release fixes a typo in the .gitignore file. diff --git a/aura-2.0/CONTRIBUTING.md b/aura-2.0/CONTRIBUTING.md new file mode 100644 index 000000000..b68f9b85c --- /dev/null +++ b/aura-2.0/CONTRIBUTING.md @@ -0,0 +1,7 @@ +# Contributing + +We are happy to review any contributions you want to make. When contributing, please follow the rules outlined at . + +The time between submitting a contribution and its review one may be extensive; do not be discouraged if there is not immediate feedback. + +Thanks! diff --git a/aura-2.0/LICENSE b/aura-2.0/LICENSE new file mode 100644 index 000000000..14f540158 --- /dev/null +++ b/aura-2.0/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2011-2015, Aura for PHP +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/aura-2.0/README.md b/aura-2.0/README.md new file mode 100644 index 000000000..1129dfa95 --- /dev/null +++ b/aura-2.0/README.md @@ -0,0 +1,345 @@ +# Aura.Web_Project + +This package provides a minimal framework for web projects. + +By "minimal" we mean *very* minimal. The package provides only a dependency +injection container, a configuration system, a router, a dispatcher, a pair of +request and response objects, and a logging instance. + +This minimal implementation should not be taken as "restrictive". The DI +container, with its two-stage configuration system, allows a wide range of +programmatic service definitions. The router and dispatcher are built with +iterative refactoring in mind, so you can start with micro-framework-like +closure controllers, and work your way into more complex controller objects of +your own design. + +## Foreword + +### Requirements + +This project requires PHP 5.4 or later; we recommend using the latest available version of PHP as a matter of principle. + +Unlike Aura library packages, this project package has userland dependencies, which themselves may have other dependencies: + +- [aura/web-kernel](https://packagist.org/packages/aura/web-kernel) +- [monolog/monolog](https://packagist.org/packages/monolog/monolog) + +### Installation + +Install this project via Composer to a `{$PROJECT_PATH}` of your choosing: + + composer create-project aura/web-project {$PROJECT_PATH} + +This will create the project skeleton and install all of the necessary packages. + +### Tests + +[![Build Status](https://travis-ci.org/auraphp/Aura.Web_Project.png)](https://travis-ci.org/auraphp/Aura.Web_Project) + +To run the unit tests at the command line, issue `./phpunit.sh` at the package root. This requires [PHPUnit](http://phpunit.de/) to be available as `phpunit`. + +Alternatively, after you have installed the project, start the built-in PHP server with the `web/` directory as the document root: + + cd {$PROJECT_PATH} + php -S localhost:8000 -t web/ + +When you browse to you should see "Hello World!" as the output. Terminate the built-in server process thereafter. (Be sure to use the built-in PHP server only for testing, never for production.) + +### PSR Compliance + +This projects attempts to comply with [PSR-1][], [PSR-2][], and [PSR-4][]. If you notice compliance oversights, please send a patch via pull request. + +[PSR-1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md +[PSR-2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md +[PSR-4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md + +### Community + +To ask questions, provide feedback, or otherwise communicate with the Aura community, please join our [Google Group](http://groups.google.com/group/auraphp), follow [@auraphp on Twitter](http://twitter.com/auraphp), or chat with us on #auraphp on Freenode. + +### Services + +This package uses services defined by: + +- [Aura.Project_Kernel](https://github.com/auraphp/Aura.Project_Kernel#services) +- [Aura.Web_Kernel](https://github.com/auraphp/Aura.Web_Kernel#services) + +This project resets the following services: + +- `aura/project-kernel:logger`: an instance of `Monolog\Logger` + +## Getting Started + +### Component Packages + +This project combines a collection of independent Aura packages into a cohesive whole. The operation of each package is documented separately. + +The dependency injection _Container_ is absolutely central to the operation of an Aura project. Please be familiar with [the Aura.Di docs](https://github.com/auraphp/Aura.Di) before continuing. + +You should also familiarize yourself with [Aura.Router](https://github.com/auraphp/Aura.Router), [Aura.Dispatcher](https://github.com/auraphp/Aura.Dispatcher), and the [Aura.Web](https://github.com/auraphp/Aura.Web) _Request_ and _Response_ objects. + +### Project Configuration + +Every Aura project is configured the same way. Please see the [shared configuration docs](https://github.com/auraphp/Aura.Project_Kernel#configuration) for more information. + +### Logging + +The project automatically logs to `{$PROJECT_PATH}/tmp/log/{$mode}.log`. If +you want to change the logging behaviors for a particular config mode, +edit the related config file (e.g., `config/Dev.php`) file to modify the +`aura/project-kernel:logger` service. + +### Routing and Dispatching + +We configure routing and dispatching via the project-level `config/` +class files. If a route needs to be available in every config mode, +edit the project-level `config/Common.php` class file. If it only needs +to be available in a specific mode, e.g. `dev`, then edit the config file for +that mode. + +Here are three different styles of routing and dispatching. + +#### Micro-Framework Style + +Aura is the first framework which follows the +[Action Domain Responder](https://github.com/pmjones/mvc-refinement) pattern. +The following is an example of a micro-framework style route, where the +action logic is embedded in the route params. In the `modifyWebRouter()` +config method, we retrieve the shared `aura/web-kernel:request` and +`aura/web-kernel:response` services, along with the `aura/web-kernel:router` +service. We then add a route names `blog.read` and embed the action code as a +closure. + +```php +get('aura/web-kernel:request'); + $response = $di->get('aura/web-kernel:response'); + + $router = $di->get('aura/web-kernel:router'); + $router + ->add('blog.read', '/blog/read/{id}') + ->addValues(array( + 'action' => function ($id) use ($request, $response) { + $content = "Reading blog post $id"; + $response->content->set(htmlspecialchars( + $content, ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8' + )); + } + )); + } + + // ... +} +?> +``` + +You can now start up the built-in PHP server to get the application running ... + + cd {$PROJECT_PATH} + php -S localhost:8000 -t web/ + +... and browse to to see the application output. + +#### Modified Micro-Framework Style + +We can modify the above example to put the controller logic in the +dispatcher instead of the route itself. + +Extract the action closure to the dispatcher under the name +`blog.read`. Then, in the route, use a `action` value that +matches the name in the dispatcher. + +```php +get('aura/web-kernel:router'); + $router + ->add('blog.read', '/blog/read/{id}') + ->addValues(array( + 'action' => 'blog.read', + )); + } + + public function modifyWebDispatcher(Container $di) + { + $request = $di->get('aura/web-kernel:request'); + $response = $di->get('aura/web-kernel:response'); + + $dispatcher = $di->get('aura/web-kernel:dispatcher'); + $dispatcher->setObject( + 'blog.read', + function ($id) use ($request, $response) { + $content = "Reading blog post $id"; + $response->content->set(htmlspecialchars( + $content, ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8' + )); + } + ); + + } + + // ... +} +?> +``` + +You can now start up the built-in PHP server to get the application running ... + + cd {$PROJECT_PATH} + php -S localhost:8000 -t web/ + +... and browse to to see the application +output. + +#### Full-Stack Style + +You can migrate from a micro style to a full-stack style (or start +with full-stack style in the first place). + +First, define an action class and place it in the project `src/` directory. + +```php +request = $request; + $this->response = $response; + } + + public function __invoke($id) + { + $content = "Reading blog post $id"; + $this->response->content->set(htmlspecialchars( + $content, ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8' + )); + } +} +?> +``` + +Next, tell the project how to build the _BlogReadAction_ through the DI +_Container_. Edit the project `config/Common.php` file to configure the +_Container_ to pass the `aura/web-kernel:request` and `aura/web-kernel:response` service objects to +the _BlogReadAction_ constructor. + +```php +set('aura/project-kernel:logger', $di->lazyNew('Monolog\Logger')); + + $di->params['App\Actions\BlogReadAction'] = array( + 'request' => $di->lazyGet('aura/web-kernel:request'), + 'response' => $di->lazyGet('aura/web-kernel:response'), + ); + } + + // ... +} +?> +``` + +After that, put the _App\Actions\BlogReadAction_ object in the dispatcher +under the name `blog.read` as a lazy-loaded instantiation ... + +```php +get('aura/web-kernel:dispatcher'); + $dispatcher->setObject( + 'blog.read', + $di->lazyNew('App\Actions\BlogReadAction') + ); + } + + // ... +} +?> +``` + +... and finally, point the router to the `blog.read` action object: + +```php +get('aura/web-kernel:router'); + $router->add('blog.read', '/blog/read/{id}'); + } + + // ... +} +?> +``` + +You can now start up the built-in PHP server to get the application +running ... + + cd {$PROJECT_PATH} + php -S localhost:8000 -t web/ + +... then browse to to see the application +output. + +#### Other Variations + +These are only some common variations of router and dispatcher interactions; +[there are many other combinations](https://github.com/auraphp/Aura.Dispatcher/tree/develop-2#refactoring-to-architecture-changes). diff --git a/aura-2.0/_benchmark/hello_world.sh b/aura-2.0/_benchmark/hello_world.sh new file mode 100644 index 000000000..410b6478d --- /dev/null +++ b/aura-2.0/_benchmark/hello_world.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +url="$base/$fw/web/index.php/hello/index" diff --git a/aura-2.0/_benchmark/setup.sh b/aura-2.0/_benchmark/setup.sh new file mode 100644 index 000000000..c6dd26c10 --- /dev/null +++ b/aura-2.0/_benchmark/setup.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +composer install --no-dev --optimize-autoloader +chmod o+w tmp/cache/ tmp/log/ diff --git a/aura-2.0/composer.json b/aura-2.0/composer.json new file mode 100644 index 000000000..f0f53aba4 --- /dev/null +++ b/aura-2.0/composer.json @@ -0,0 +1,42 @@ +{ + "name": "aura/web-project", + "type": "project", + "description": "A minimal web framework built from Aura v2 packages", + "keywords": [ + "web", + "framework" + ], + "license": "BSD-2-Clause", + "authors": [ + { + "name": "Aura.Web_Project Contributors", + "homepage": "https://github.com/auraphp/Aura.Web_Project/contributors" + } + ], + "require": { + "aura/web-kernel": "2.0.*", + "monolog/monolog": "~1.0" + }, + "autoload": { + "psr-4": { + "": "src/", + "Aura\\Web_Project\\_Config\\": "config/" + } + }, + "extra": { + "aura": { + "type": "project", + "config": { + "common": "Aura\\Web_Project\\_Config\\Common", + "dev": "Aura\\Web_Project\\_Config\\Dev", + "test": "Aura\\Web_Project\\_Config\\Test", + "prod": "Aura\\Web_Project\\_Config\\Prod" + } + } + }, + "autoload-dev": { + "psr-4": { + "Aura\\Web_Project\\": "tests/" + } + } +} diff --git a/aura-2.0/composer.lock b/aura-2.0/composer.lock new file mode 100644 index 000000000..f075724ed --- /dev/null +++ b/aura-2.0/composer.lock @@ -0,0 +1,460 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "d09fe0081ee1f815da8ee7ffbdcb6966", + "content-hash": "9242351f6b14e443e865fd9edbd0d7c9", + "packages": [ + { + "name": "aura/di", + "version": "2.2.4", + "source": { + "type": "git", + "url": "https://github.com/auraphp/Aura.Di.git", + "reference": "81d5d9c602ca292a16e32001dcbd2adab5350e28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/auraphp/Aura.Di/zipball/81d5d9c602ca292a16e32001dcbd2adab5350e28", + "reference": "81d5d9c602ca292a16e32001dcbd2adab5350e28", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "aura": { + "type": "library" + } + }, + "autoload": { + "psr-4": { + "Aura\\Di\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Aura.Di Contributors", + "homepage": "https://github.com/auraphp/Aura.Di/contributors" + } + ], + "description": "Provides a dependency injection container system with native support for constructor- and setter-based injection, lazy-loading of services, and inheritable configuration of setters and constructor params.", + "homepage": "https://github.com/auraphp/Aura.Di", + "keywords": [ + "container", + "dependency injection", + "dependency injection container", + "di", + "di container" + ], + "time": "2016-01-21 09:34:53" + }, + { + "name": "aura/dispatcher", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/auraphp/Aura.Dispatcher.git", + "reference": "d84a58d00788220077b7281f293f748470eabec5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/auraphp/Aura.Dispatcher/zipball/d84a58d00788220077b7281f293f748470eabec5", + "reference": "d84a58d00788220077b7281f293f748470eabec5", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "type": "library", + "extra": { + "aura": { + "type": "library" + } + }, + "autoload": { + "psr-4": { + "Aura\\Dispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Aura.Dispatcher Contributors", + "homepage": "https://github.com/auraphp/Aura.Dispatcher/contributors" + } + ], + "description": "Creates objects from a factory and invokes methods using named parameters; also provides a trait for invoking closures and object methods with named parameters.", + "homepage": "https://github.com/auraphp/Aura.Dispatcher", + "keywords": [ + "controller", + "dispatcher", + "factory" + ], + "time": "2016-10-03 19:45:52" + }, + { + "name": "aura/project-kernel", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/auraphp/Aura.Project_Kernel.git", + "reference": "644475dbb4859c24aeeb309fd76a9c3e644ba9be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/auraphp/Aura.Project_Kernel/zipball/644475dbb4859c24aeeb309fd76a9c3e644ba9be", + "reference": "644475dbb4859c24aeeb309fd76a9c3e644ba9be", + "shasum": "" + }, + "require": { + "aura/di": "~2.0", + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "type": "library", + "extra": { + "aura": { + "type": "kernel", + "config": { + "common": "Aura\\Project_Kernel\\_Config\\Common" + } + }, + "branch-alias": { + "dev-develop-2": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Aura\\Project_Kernel\\": "src/", + "Aura\\Project_Kernel\\_Config\\": "config/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Aura.Project_Kernel Contributors", + "homepage": "https://github.com/auraphp/Aura.Project_Kernel/contributors" + } + ], + "description": "The shared kernel files for an Aura project.", + "homepage": "https://github.com/auraphp/Aura.Project_Kernel", + "keywords": [ + "kernel", + "project" + ], + "time": "2015-03-27 22:29:48" + }, + { + "name": "aura/router", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/auraphp/Aura.Router.git", + "reference": "d8cb21b7fa8d2b2ae874093d5440688b3356f9c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/auraphp/Aura.Router/zipball/d8cb21b7fa8d2b2ae874093d5440688b3356f9c4", + "reference": "d8cb21b7fa8d2b2ae874093d5440688b3356f9c4", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "aura/di": "~2.0" + }, + "type": "library", + "extra": { + "aura": { + "type": "library", + "config": { + "common": "Aura\\Router\\_Config\\Common" + } + } + }, + "autoload": { + "psr-4": { + "Aura\\Router\\": "src/", + "Aura\\Router\\_Config\\": "config/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Aura.Router Contributors", + "homepage": "https://github.com/auraphp/Aura.Router/contributors" + } + ], + "description": "A web router implementation; given a URI path and a copy of $_SERVER, it will extract path-info parameter values for a specific route.", + "homepage": "https://github.com/auraphp/Aura.Router", + "keywords": [ + "route", + "router", + "routing" + ], + "time": "2016-10-03 20:00:15" + }, + { + "name": "aura/web", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/auraphp/Aura.Web.git", + "reference": "6aa9f316e8d6f7f6e29577aa3c7bf4493a89bdd6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/auraphp/Aura.Web/zipball/6aa9f316e8d6f7f6e29577aa3c7bf4493a89bdd6", + "reference": "6aa9f316e8d6f7f6e29577aa3c7bf4493a89bdd6", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "aura/di": "~2.0" + }, + "type": "library", + "extra": { + "aura": { + "type": "library", + "config": { + "common": "Aura\\Web\\_Config\\Common" + } + } + }, + "autoload": { + "psr-4": { + "Aura\\Web\\": "src/", + "Aura\\Web\\_Config\\": "config/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Aura.Web Contributors", + "homepage": "https://github.com/auraphp/Aura.Web/contributors" + } + ], + "description": "Provides web Request and Response objects for use by web controllers and actions. These are representations of the PHP web environment, not HTTP request and response objects proper.", + "homepage": "https://github.com/auraphp/Aura.Web", + "keywords": [ + "request", + "response" + ], + "time": "2017-02-09 17:05:11" + }, + { + "name": "aura/web-kernel", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/auraphp/Aura.Web_Kernel.git", + "reference": "09b66bdfcd96c5082458f79ee41974501079efbf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/auraphp/Aura.Web_Kernel/zipball/09b66bdfcd96c5082458f79ee41974501079efbf", + "reference": "09b66bdfcd96c5082458f79ee41974501079efbf", + "shasum": "" + }, + "require": { + "aura/dispatcher": "~2.0", + "aura/project-kernel": "~2.0", + "aura/router": "~2.0", + "aura/web": "~2.0", + "php": ">=5.4.0" + }, + "type": "library", + "extra": { + "aura": { + "type": "kernel", + "config": { + "common": "Aura\\Web_Kernel\\_Config\\Common", + "web-kernel-test": "Aura\\Web_Kernel\\_Config\\WebKernelTest" + } + }, + "branch-alias": { + "dev-develop-2": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Aura\\Web_Kernel\\": "src/", + "Aura\\Web_Kernel\\_Config\\": "config/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Aura.Web_Kernel Contributors", + "homepage": "https://github.com/auraphp/Aura.Web_Kernel/contributors" + } + ], + "description": "The kernel files for an Aura web project.", + "homepage": "https://github.com/auraphp/Aura.Web_Kernel", + "keywords": [ + "kernel", + "web" + ], + "time": "2014-11-06 21:57:11" + }, + { + "name": "monolog/monolog", + "version": "1.22.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "bad29cb8d18ab0315e6c477751418a82c850d558" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bad29cb8d18ab0315e6c477751418a82c850d558", + "reference": "bad29cb8d18ab0315e6c477751418a82c850d558", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "ruflin/elastica": ">=0.90 <3.0", + "sentry/sentry": "^0.13", + "swiftmailer/swiftmailer": "~5.3" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "time": "2016-11-26 00:15:39" + }, + { + "name": "psr/log", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10 12:19:37" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/aura-2.0/config/Common.php b/aura-2.0/config/Common.php new file mode 100644 index 000000000..96a304c72 --- /dev/null +++ b/aura-2.0/config/Common.php @@ -0,0 +1,71 @@ +set('aura/project-kernel:logger', $di->lazyNew('Monolog\Logger')); + + $di->params['App\Actions\Hello'] = array( + 'request' => $di->lazyGet('aura/web-kernel:request'), + 'response' => $di->lazyGet('aura/web-kernel:response'), + ); + } + + public function modify(Container $di) + { + $this->modifyLogger($di); + $this->modifyWebRouter($di); + $this->modifyWebDispatcher($di); + + $dispatcher = $di->get('aura/web-kernel:dispatcher'); + $dispatcher->setObject( + 'hello', + $di->lazyNew('App\Actions\Hello') + ); + + $router = $di->get('aura/web-kernel:router'); + $router + ->add('hello', '/hello/index') + ->addValues(array( + 'action' => 'hello', + )); + } + + public function modifyLogger(Container $di) + { + $project = $di->get('project'); + $mode = $project->getMode(); + $file = $project->getPath("tmp/log/{$mode}.log"); + + $logger = $di->get('aura/project-kernel:logger'); + $logger->pushHandler($di->newInstance( + 'Monolog\Handler\StreamHandler', + array( + 'stream' => $file, + ) + )); + } + + public function modifyWebRouter(Container $di) + { + $router = $di->get('aura/web-kernel:router'); + + $router->add('hello', '/') + ->setValues(array('action' => 'hello')); + } + + public function modifyWebDispatcher($di) + { + $dispatcher = $di->get('aura/web-kernel:dispatcher'); + + $dispatcher->setObject('hello', function () use ($di) { + $response = $di->get('aura/web-kernel:response'); + $response->content->set('Hello World!'); + }); + } +} diff --git a/aura-2.0/config/Dev.php b/aura-2.0/config/Dev.php new file mode 100644 index 000000000..7ae1ecf9c --- /dev/null +++ b/aura-2.0/config/Dev.php @@ -0,0 +1,18 @@ + + + + ./tests + + + diff --git a/fuel-1.8-dev/fuel/app/cache/.gitkeep b/aura-2.0/src/.placeholder similarity index 100% rename from fuel-1.8-dev/fuel/app/cache/.gitkeep rename to aura-2.0/src/.placeholder diff --git a/aura-2.0/src/App/Actions/Hello.php b/aura-2.0/src/App/Actions/Hello.php new file mode 100644 index 000000000..3dd935ff2 --- /dev/null +++ b/aura-2.0/src/App/Actions/Hello.php @@ -0,0 +1,22 @@ +request = $request; + $this->response = $response; + } + + public function __invoke() + { + $this->response->content->set( + 'Hello World!' + ); + } +} diff --git a/aura-2.0/tests/WebProjectTest.php b/aura-2.0/tests/WebProjectTest.php new file mode 100644 index 000000000..4dd43a0aa --- /dev/null +++ b/aura-2.0/tests/WebProjectTest.php @@ -0,0 +1,13 @@ +assertSame($expect, $actual); + } +} diff --git a/fuel-1.8-dev/fuel/app/classes/controller/.gitkeep b/aura-2.0/tmp/.placeholder old mode 100644 new mode 100755 similarity index 100% rename from fuel-1.8-dev/fuel/app/classes/controller/.gitkeep rename to aura-2.0/tmp/.placeholder diff --git a/fuel-1.8-dev/fuel/app/classes/model/.gitkeep b/aura-2.0/tmp/cache/.placeholder old mode 100644 new mode 100755 similarity index 100% rename from fuel-1.8-dev/fuel/app/classes/model/.gitkeep rename to aura-2.0/tmp/cache/.placeholder diff --git a/fuel-1.8-dev/fuel/app/lang/en/.gitkeep b/aura-2.0/tmp/log/.placeholder old mode 100644 new mode 100755 similarity index 100% rename from fuel-1.8-dev/fuel/app/lang/en/.gitkeep rename to aura-2.0/tmp/log/.placeholder diff --git a/aura-2.0/web/index.php b/aura-2.0/web/index.php new file mode 100644 index 000000000..78a5551cf --- /dev/null +++ b/aura-2.0/web/index.php @@ -0,0 +1,19 @@ +newKernel( + $path, + 'Aura\Web_Kernel\WebKernel' +); +$kernel(); + +require $_SERVER['DOCUMENT_ROOT'].'/php-framework-benchmark/libs/output_data.php'; diff --git a/bear-0.10/_benchmark/hello_world.sh b/bear-0.10/_benchmark/hello_world.sh new file mode 100644 index 000000000..bf7861999 --- /dev/null +++ b/bear-0.10/_benchmark/hello_world.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +url="$base/$fw/var/www/index.php/hello" diff --git a/bear-0.10/_benchmark/setup.sh b/bear-0.10/_benchmark/setup.sh new file mode 100644 index 000000000..a93fba845 --- /dev/null +++ b/bear-0.10/_benchmark/setup.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +composer install --no-dev --optimize-autoloader +chmod o+w var/tmp/ var/log/ diff --git a/bear-0.10/bootstrap/contexts/prod.php b/bear-0.10/bootstrap/contexts/prod.php index 816dc1be7..c8ed31fce 100644 --- a/bear-0.10/bootstrap/contexts/prod.php +++ b/bear-0.10/bootstrap/contexts/prod.php @@ -65,7 +65,7 @@ // OK: { $app->response->setResource($app->page)->render()->send(); - echo "\n" . (memory_get_peak_usage(true)/1024/1024); + require $_SERVER['DOCUMENT_ROOT'].'/php-framework-benchmark/libs/output_data.php'; exit(0); } diff --git a/bear-0.10/var/www/.htaccess b/bear-0.10/var/www/.htaccess deleted file mode 100644 index 89cf2721d..000000000 --- a/bear-0.10/var/www/.htaccess +++ /dev/null @@ -1,23 +0,0 @@ -order deny,allow - - - order allow,deny - deny from all - - - order allow,deny - deny from all - - - - # turn on rewriting - RewriteEngine On - - # for all files not found in the file system, - # reroute to "index.php" bootstrap script, - # keeping the query string intact. - RewriteCond %{REQUEST_FILENAME} !-d - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !favicon.ico$ - RewriteRule ^(.*)$ /index.php [QSA,L] - \ No newline at end of file diff --git a/bear-1.0/_benchmark/hello_world.sh b/bear-1.0/_benchmark/hello_world.sh new file mode 100644 index 000000000..bf7861999 --- /dev/null +++ b/bear-1.0/_benchmark/hello_world.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +url="$base/$fw/var/www/index.php/hello" diff --git a/bear-1.0/_benchmark/setup.sh b/bear-1.0/_benchmark/setup.sh new file mode 100644 index 000000000..a93fba845 --- /dev/null +++ b/bear-1.0/_benchmark/setup.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +composer install --no-dev --optimize-autoloader +chmod o+w var/tmp/ var/log/ diff --git a/bear-1.0/bootstrap/api.php b/bear-1.0/bootstrap/api.php index 50b2d7204..d175a02ac 100644 --- a/bear-1.0/bootstrap/api.php +++ b/bear-1.0/bootstrap/api.php @@ -1,4 +1,4 @@ newApp(new AppMeta(__NAMESPACE__), $context); + $app = (new Bootstrap)->getApp(__NAMESPACE__, $context); + /* @var $app AbstractApp \BEAR\Sunday\Extension\Application\AbstractApp */ $request = $app->router->match($GLOBALS, $_SERVER); } -// Adhoc fix for benchmarking -// Remove sub directories from URI -$pagePath = preg_replace('!/php-framework-benchmark/bear-1.0/var/www/index.php!', '', $request->path); -//var_dump($pagePath); exit; - try { - /** @var $page \BEAR\Resource\Request */ + // resource request $page = $app->resource ->{$request->method} - ->uri($pagePath) + ->uri($request->path) ->withQuery($request->query) ->request(); + /* @var $page \BEAR\Resource\Request */ // representation transfer $page()->transfer($app->responder, $_SERVER); - echo "\n" . (memory_get_peak_usage(true)/1024/1024); + require $_SERVER['DOCUMENT_ROOT'].'/php-framework-benchmark/libs/output_data.php'; exit(0); } catch (\Exception $e) { $app->error->handle($e, $request)->transfer(); diff --git a/bear-1.0/composer.json b/bear-1.0/composer.json index 71c1bf296..c0c64086e 100644 --- a/bear-1.0/composer.json +++ b/bear-1.0/composer.json @@ -3,7 +3,7 @@ "description":"n/a", "license": "proprietary", "require": { - "bear/package": "~1.0@dev" + "bear/package": "1.0.*" }, "require-dev": { }, @@ -13,10 +13,5 @@ "psr-4": { "My\\Hello\\": "src/" } - }, - "extra": { - "branch-alias": { - "dev-master": "0.1.x-dev" - } } } diff --git a/bear-1.0/composer.lock b/bear-1.0/composer.lock index 29a1b8fba..32a6ba0a6 100644 --- a/bear-1.0/composer.lock +++ b/bear-1.0/composer.lock @@ -1,28 +1,32 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "76864240da187c1197a8ad44ab7541f9", + "hash": "1d239010803158f1515018c5a06dcedf", + "content-hash": "514e561e6ec7ab33f2605d72b66f1020", "packages": [ { "name": "aura/cli", - "version": "2.0.4", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/auraphp/Aura.Cli.git", - "reference": "b8cd7b3a435f25082fd5c174cffab375113c2838" + "reference": "d69cfa6d81e94e13d831e38ad9e3d53aa0f08a8b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/auraphp/Aura.Cli/zipball/b8cd7b3a435f25082fd5c174cffab375113c2838", - "reference": "b8cd7b3a435f25082fd5c174cffab375113c2838", + "url": "https://api.github.com/repos/auraphp/Aura.Cli/zipball/d69cfa6d81e94e13d831e38ad9e3d53aa0f08a8b", + "reference": "d69cfa6d81e94e13d831e38ad9e3d53aa0f08a8b", "shasum": "" }, "require": { "php": ">=5.3.0" }, + "require-dev": { + "aura/di": "~2.0" + }, "type": "library", "extra": { "aura": { @@ -30,9 +34,6 @@ "config": { "common": "Aura\\Cli\\_Config\\Common" } - }, - "branch-alias": { - "dev-develop-2": "2.0.x-dev" } }, "autoload": { @@ -61,139 +62,29 @@ "options", "stdio" ], - "time": "2015-03-16 14:42:45" - }, - { - "name": "aura/router", - "version": "2.2.1", - "source": { - "type": "git", - "url": "https://github.com/auraphp/Aura.Router.git", - "reference": "61003d545bcaf5b7f3a40c8b60c2b963a8f0ce19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/auraphp/Aura.Router/zipball/61003d545bcaf5b7f3a40c8b60c2b963a8f0ce19", - "reference": "61003d545bcaf5b7f3a40c8b60c2b963a8f0ce19", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "aura": { - "type": "library", - "config": { - "common": "Aura\\Router\\_Config\\Common" - } - }, - "branch-alias": { - "dev-develop-2": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Aura\\Router\\": "src/", - "Aura\\Router\\_Config\\": "config/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Aura.Router Contributors", - "homepage": "https://github.com/auraphp/Aura.Router/contributors" - } - ], - "description": "A web router implementation; given a URI path and a copy of $_SERVER, it will extract path-info parameter values for a specific route.", - "homepage": "https://github.com/auraphp/Aura.Router", - "keywords": [ - "route", - "router", - "routing" - ], - "time": "2015-03-15 19:41:14" - }, - { - "name": "aura/web", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/auraphp/Aura.Web.git", - "reference": "38b07f7f4f52d0f86e2fcab924fe2769f1f056d7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/auraphp/Aura.Web/zipball/38b07f7f4f52d0f86e2fcab924fe2769f1f056d7", - "reference": "38b07f7f4f52d0f86e2fcab924fe2769f1f056d7", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "aura": { - "type": "library", - "config": { - "common": "Aura\\Web\\_Config\\Common" - } - }, - "branch-alias": { - "dev-develop-2": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Aura\\Web\\": "src/", - "Aura\\Web\\_Config\\": "config/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Aura.Web Contributors", - "homepage": "https://github.com/auraphp/Aura.Web/contributors" - } - ], - "description": "Provides web Request and Response objects for use by web controllers and actions. These are representations of the PHP web environment, not HTTP request and response objects proper.", - "homepage": "https://github.com/auraphp/Aura.Web", - "keywords": [ - "request", - "response" - ], - "time": "2015-03-15 19:12:55" + "time": "2017-02-09 17:14:27" }, { "name": "bear/app-meta", - "version": "0.1.1", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/BEARSunday/BEAR.AppMeta.git", - "reference": "f8410ec824853570274246a45a0b94b869d337fc" + "url": "https://github.com/bearsunday/BEAR.AppMeta.git", + "reference": "ddf12fca79d7cc38a535b7cc6b021d0919147796" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/BEARSunday/BEAR.AppMeta/zipball/f8410ec824853570274246a45a0b94b869d337fc", - "reference": "f8410ec824853570274246a45a0b94b869d337fc", + "url": "https://api.github.com/repos/bearsunday/BEAR.AppMeta/zipball/ddf12fca79d7cc38a535b7cc6b021d0919147796", + "reference": "ddf12fca79d7cc38a535b7cc6b021d0919147796", "shasum": "" }, "require": { - "bear/resource": "~1.0@dev", - "koriym/psr4list": "~1.0@dev" + "koriym/psr4list": "~1.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-develop": "0.1.x-dev" - } + "require-dev": { + "bear/resource": "~1.0" }, + "type": "library", "autoload": { "psr-4": { "BEAR\\AppMeta\\": "src/" @@ -201,47 +92,40 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "description": "BEAR.Sunday application meta information", "keywords": [ - "BEARSunday" + "BEAR.Sunday" ], - "time": "2015-02-17 16:23:25" + "time": "2015-05-19 09:52:02" }, { "name": "bear/package", - "version": "1.0.0-alpha.4", + "version": "1.0.10", "source": { "type": "git", "url": "https://github.com/bearsunday/BEAR.Package.git", - "reference": "60486e85851cdd9f73f48699cbb1f2328ed0da69" + "reference": "1087dd3a9e4709258a3e94a54405050929e0f3ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bearsunday/BEAR.Package/zipball/60486e85851cdd9f73f48699cbb1f2328ed0da69", - "reference": "60486e85851cdd9f73f48699cbb1f2328ed0da69", + "url": "https://api.github.com/repos/bearsunday/BEAR.Package/zipball/1087dd3a9e4709258a3e94a54405050929e0f3ed", + "reference": "1087dd3a9e4709258a3e94a54405050929e0f3ed", "shasum": "" }, "require": { "aura/cli": "~2.0", - "aura/router": "~2.0", - "aura/web": "~2.0", - "bear/app-meta": "~0.1", - "bear/query-repository": "~0.1", + "bear/app-meta": "~1.0", + "bear/query-repository": "~1.0", "bear/sunday": "~1.0", "php": ">=5.5.0", - "ray/web-param-module": "~0.1" + "ray/web-param-module": "~1.0" }, "bin": [ "bin/bear.env" ], "type": "library", - "extra": { - "branch-alias": { - "dev-develop-2": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "BEAR\\Package\\": "src/" @@ -249,7 +133,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { @@ -268,42 +152,37 @@ "hypermedia", "rest" ], - "time": "2015-03-11 04:51:37" + "time": "2016-10-05 08:26:59" }, { "name": "bear/query-repository", - "version": "0.2.1", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/bearsunday/BEAR.QueryRepository.git", - "reference": "fea591e64f154f8b65930cefffcf5af16c1f127f" + "reference": "abf8ac4f4d5a7d6c368d178433c51d0e2832f9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bearsunday/BEAR.QueryRepository/zipball/fea591e64f154f8b65930cefffcf5af16c1f127f", - "reference": "fea591e64f154f8b65930cefffcf5af16c1f127f", + "url": "https://api.github.com/repos/bearsunday/BEAR.QueryRepository/zipball/abf8ac4f4d5a7d6c368d178433c51d0e2832f9dc", + "reference": "abf8ac4f4d5a7d6c368d178433c51d0e2832f9dc", "shasum": "" }, "require": { - "bear/resource": "~1.0@dev" + "bear/resource": "~1.2", + "mobiledetect/mobiledetectlib": "~2.0", + "php": ">=5.5.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-develop": "0.1.x-dev" - } - }, "autoload": { "psr-4": { - "BEAR\\QueryRepository\\": "src/" - }, - "files": [ - "src-file/doctrine_annotations.php" - ] + "BEAR\\QueryRepository\\": "src/", + "BEAR\\RepositoryModule\\Annotation\\": "src-annotation/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { @@ -312,50 +191,44 @@ } ], "description": "Resource query responsibility segregation", - "time": "2015-03-11 02:36:49" + "time": "2016-08-10 02:42:16" }, { "name": "bear/resource", - "version": "1.0.1", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/bearsunday/BEAR.Resource.git", - "reference": "ee21dfe6ea02e28d13e1b2bc7afefdb1c7b69fce" + "reference": "6189cab8ac33b324c13a9bc6609b0c5029621bf6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bearsunday/BEAR.Resource/zipball/ee21dfe6ea02e28d13e1b2bc7afefdb1c7b69fce", - "reference": "ee21dfe6ea02e28d13e1b2bc7afefdb1c7b69fce", + "url": "https://api.github.com/repos/bearsunday/BEAR.Resource/zipball/6189cab8ac33b324c13a9bc6609b0c5029621bf6", + "reference": "6189cab8ac33b324c13a9bc6609b0c5029621bf6", "shasum": "" }, "require": { "doctrine/cache": "~1.0", "nocarrier/hal": "~0.9", - "php": ">=5.5.0", - "ray/di": "~2.0", - "rize/uri-template": "~0.2.5" + "php": ">=5.6.0", + "ray/di": "^2.4.1", + "rize/uri-template": "~0.2" }, "suggest": { "ext-uri_template": "ext/uri_template for URI Template(RFC6570) specification." }, "type": "library", - "extra": { - "branch-alias": { - "dev-develop-2": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "BEAR\\Resource\\": "src/" }, "files": [ - "src-files/uri_template.php", - "src-files/doctrine_annotations.php" + "src-files/uri_template.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { @@ -364,7 +237,6 @@ } ], "description": "Hypermedia framework for object as a service", - "homepage": "https://github.com/koriym/BEAR.Resource", "keywords": [ "HATEOAS", "Object as a service", @@ -372,34 +244,29 @@ "protocol", "rest" ], - "time": "2015-03-24 03:41:41" + "time": "2016-10-03 15:19:07" }, { "name": "bear/sunday", - "version": "1.0.0-rc.2", + "version": "1.0.5", "source": { "type": "git", "url": "https://github.com/bearsunday/BEAR.Sunday.git", - "reference": "c32ddf261af41e9c3d839fe1ba5b58bf6c075142" + "reference": "2d6a47a286627b5da35d3bf2db84c676f397f843" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bearsunday/BEAR.Sunday/zipball/c32ddf261af41e9c3d839fe1ba5b58bf6c075142", - "reference": "c32ddf261af41e9c3d839fe1ba5b58bf6c075142", + "url": "https://api.github.com/repos/bearsunday/BEAR.Sunday/zipball/2d6a47a286627b5da35d3bf2db84c676f397f843", + "reference": "2d6a47a286627b5da35d3bf2db84c676f397f843", "shasum": "" }, "require": { - "bear/resource": "~1.0", + "bear/resource": "~1.3", "doctrine/cache": "~1.0", "php": ">=5.5.0", "psr/log": "~1.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-develop-2": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "BEAR\\Sunday\\": "src/" @@ -407,56 +274,61 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { "name": "Akihito Koriyama", - "email": "akihito.koriyama@gmail.com" + "email": "akihito.koriyama@gmail.com", + "homepage": "https://koriym.github.io/" + }, + { + "name": "BEAR.Sunday Contributors", + "homepage": "http://bearsunday.github.io/contributors.html" } ], - "description": "Resource oriented framework", - "homepage": "https://github.com/koriym/BEAR.Sunday", + "description": "A resource oriented framework", "keywords": [ "aop", "di", "framework", "hypermedia", + "ray", "rest" ], - "time": "2015-03-24 01:47:36" + "time": "2016-10-05 06:43:55" }, { "name": "doctrine/annotations", - "version": "v1.2.3", + "version": "v1.3.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "eeda578cbe24a170331a1cfdf78be723412df7a4" + "reference": "bd4461328621bde0ae6b1b2675fbc6aca4ceb558" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/eeda578cbe24a170331a1cfdf78be723412df7a4", - "reference": "eeda578cbe24a170331a1cfdf78be723412df7a4", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/bd4461328621bde0ae6b1b2675fbc6aca4ceb558", + "reference": "bd4461328621bde0ae6b1b2675fbc6aca4ceb558", "shasum": "" }, "require": { "doctrine/lexer": "1.*", - "php": ">=5.3.2" + "php": "^5.6 || ^7.0" }, "require-dev": { "doctrine/cache": "1.*", - "phpunit/phpunit": "4.*" + "phpunit/phpunit": "^5.6.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "1.4.x-dev" } }, "autoload": { - "psr-0": { - "Doctrine\\Common\\Annotations\\": "lib/" + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" } }, "notification-url": "https://packagist.org/downloads/", @@ -492,42 +364,42 @@ "docblock", "parser" ], - "time": "2014-12-20 20:49:38" + "time": "2016-12-30 15:59:45" }, { "name": "doctrine/cache", - "version": "v1.4.0", + "version": "v1.6.1", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "2346085d2b027b233ae1d5de59b07440b9f288c8" + "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/2346085d2b027b233ae1d5de59b07440b9f288c8", - "reference": "2346085d2b027b233ae1d5de59b07440b9f288c8", + "url": "https://api.github.com/repos/doctrine/cache/zipball/b6f544a20f4807e81f7044d31e679ccbb1866dc3", + "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "~5.5|~7.0" }, "conflict": { "doctrine/common": ">2.2,<2.4" }, "require-dev": { - "phpunit/phpunit": ">=3.7", - "predis/predis": "~0.8", + "phpunit/phpunit": "~4.8|~5.0", + "predis/predis": "~1.0", "satooshi/php-coveralls": "~0.6" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "1.6.x-dev" } }, "autoload": { - "psr-0": { - "Doctrine\\Common\\Cache\\": "lib/" + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" } }, "notification-url": "https://packagist.org/downloads/", @@ -562,7 +434,7 @@ "cache", "caching" ], - "time": "2015-01-15 20:38:55" + "time": "2016-10-29 11:16:17" }, { "name": "doctrine/lexer", @@ -618,6 +490,56 @@ ], "time": "2014-09-09 13:34:57" }, + { + "name": "koriym/printo", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/koriym/print_o.git", + "reference": "d969e4cc738a376d479c2125e7d62be88cfa9dbe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/koriym/print_o/zipball/d969e4cc738a376d479c2125e7d62be88cfa9dbe", + "reference": "d969e4cc738a376d479c2125e7d62be88cfa9dbe", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Koriym\\Printo\\": "src/" + }, + "files": [ + "src/print_o.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Akihito Koriyama", + "email": "akihito.koriyama@gmail.com", + "homepage": "https://github.com/koriym" + } + ], + "description": "An object graph visualizer.", + "homepage": "https://github.com/koriym/print_o", + "keywords": [ + "debug", + "var_dump" + ], + "time": "2015-02-13 19:04:21" + }, { "name": "koriym/psr4list", "version": "1.0", @@ -653,34 +575,94 @@ ], "time": "2014-12-07 13:10:31" }, + { + "name": "mobiledetect/mobiledetectlib", + "version": "2.8.24", + "source": { + "type": "git", + "url": "https://github.com/serbanghita/Mobile-Detect.git", + "reference": "cdf8f8efaf993bc687e78e4622f5eebd0b8b3bf3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/cdf8f8efaf993bc687e78e4622f5eebd0b8b3bf3", + "reference": "cdf8f8efaf993bc687e78e4622f5eebd0b8b3bf3", + "shasum": "" + }, + "require": { + "php": ">=5.0.0" + }, + "require-dev": { + "codeclimate/php-test-reporter": "dev-master", + "johnkary/phpunit-speedtrap": "~1.0@dev", + "phpunit/phpunit": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "Mobile_Detect.php" + ], + "psr-0": { + "Detection": "namespaced/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Serban Ghita", + "email": "serbanghita@gmail.com", + "homepage": "http://mobiledetect.net", + "role": "Developer" + } + ], + "description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.", + "homepage": "https://github.com/serbanghita/Mobile-Detect", + "keywords": [ + "detect mobile devices", + "mobile", + "mobile detect", + "mobile detector", + "php mobile detect" + ], + "time": "2016-11-11 14:56:25" + }, { "name": "nikic/php-parser", - "version": "v1.2.1", + "version": "v2.1.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "dba7524b3724f25b947cd26a580787c55c8a6f9b" + "reference": "4dd659edadffdc2143e4753df655d866dbfeedf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dba7524b3724f25b947cd26a580787c55c8a6f9b", - "reference": "dba7524b3724f25b947cd26a580787c55c8a6f9b", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4dd659edadffdc2143e4753df655d866dbfeedf0", + "reference": "4dd659edadffdc2143e4753df655d866dbfeedf0", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=5.3" + "php": ">=5.4" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" }, + "bin": [ + "bin/php-parse" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "2.1-dev" } }, "autoload": { - "files": [ - "lib/bootstrap.php" - ] + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -696,27 +678,27 @@ "parser", "php" ], - "time": "2015-03-24 19:10:28" + "time": "2016-09-16 12:04:44" }, { "name": "nocarrier/hal", - "version": "0.9.10", + "version": "0.9.12", "source": { "type": "git", "url": "https://github.com/blongden/hal.git", - "reference": "4ff57176cb4ebde4245bda979ed662a0f8480b97" + "reference": "08ffddf52733633403e0f4ca6e949b0c7cc186f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/blongden/hal/zipball/4ff57176cb4ebde4245bda979ed662a0f8480b97", - "reference": "4ff57176cb4ebde4245bda979ed662a0f8480b97", + "url": "https://api.github.com/repos/blongden/hal/zipball/08ffddf52733633403e0f4ca6e949b0c7cc186f8", + "reference": "08ffddf52733633403e0f4ca6e949b0c7cc186f8", "shasum": "" }, "require": { "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "4.*" + "phpunit/phpunit": "4.4.*" }, "type": "library", "autoload": { @@ -744,26 +726,34 @@ "rest", "xml" ], - "time": "2014-11-24 11:48:52" + "time": "2015-09-07 11:14:47" }, { "name": "psr/log", - "version": "1.0.0", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", "shasum": "" }, + "require": { + "php": ">=5.3.0" + }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { - "psr-0": { - "Psr\\Log\\": "" + "psr-4": { + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", @@ -777,41 +767,37 @@ } ], "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ "log", "psr", "psr-3" ], - "time": "2012-12-21 11:40:51" + "time": "2016-10-10 12:19:37" }, { "name": "ray/aop", - "version": "2.0.0", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/ray-di/Ray.Aop.git", - "reference": "fdd97c2d59d7e2cb0a12399a583b2f38d63142e9" + "reference": "8b1a9a8efbcd5a7569f566cad7ccd694f6a315ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ray-di/Ray.Aop/zipball/fdd97c2d59d7e2cb0a12399a583b2f38d63142e9", - "reference": "fdd97c2d59d7e2cb0a12399a583b2f38d63142e9", + "url": "https://api.github.com/repos/ray-di/Ray.Aop/zipball/8b1a9a8efbcd5a7569f566cad7ccd694f6a315ff", + "reference": "8b1a9a8efbcd5a7569f566cad7ccd694f6a315ff", "shasum": "" }, "require": { - "doctrine/annotations": "~1.1", - "nikic/php-parser": "~1.0", - "php": ">=5.4.0" + "doctrine/annotations": "^1.2.7", + "nikic/php-parser": "~1.3||~2.0", + "php": ">=5.6.0" }, "suggest": { "ray/di": "A dependency injection framework with Ray.Aop" }, "type": "library", - "extra": { - "branch-alias": { - "dev-develop-2": "2.0.x-dev" - } - }, "autoload": { "psr-4": { "Ray\\Aop\\": "src/" @@ -819,7 +805,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { @@ -835,32 +821,66 @@ "aop", "code-gen" ], - "time": "2015-02-28 11:17:42" + "time": "2016-08-28 12:56:48" }, { - "name": "ray/di", - "version": "2.0.0", + "name": "ray/compiler", + "version": "1.0.5", "source": { "type": "git", - "url": "https://github.com/ray-di/Ray.Di.git", - "reference": "0b8f613a8796cad530ddf6562ff4806349b5fea5" + "url": "https://github.com/ray-di/Ray.Compiler.git", + "reference": "8c1d1f92b34694f1a990e38d03f184ca35c5268c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ray-di/Ray.Di/zipball/0b8f613a8796cad530ddf6562ff4806349b5fea5", - "reference": "0b8f613a8796cad530ddf6562ff4806349b5fea5", + "url": "https://api.github.com/repos/ray-di/Ray.Compiler/zipball/8c1d1f92b34694f1a990e38d03f184ca35c5268c", + "reference": "8c1d1f92b34694f1a990e38d03f184ca35c5268c", "shasum": "" }, "require": { - "php": ">=5.4.0", - "ray/aop": "~2.0" + "koriym/printo": "~1.0" + }, + "require-dev": { + "ray/di": "~2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-develop-2": "2.0.x-dev" + "autoload": { + "psr-4": { + "Ray\\Compiler\\": "src/" } }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A dependency injection compiler for Ray.Di", + "keywords": [ + "Ray.Di", + "code-gen", + "compiler" + ], + "time": "2016-10-16 04:18:38" + }, + { + "name": "ray/di", + "version": "2.4.1", + "source": { + "type": "git", + "url": "https://github.com/ray-di/Ray.Di.git", + "reference": "26569fae113ae46a3d8a048533ef97e88fa995e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ray-di/Ray.Di/zipball/26569fae113ae46a3d8a048533ef97e88fa995e9", + "reference": "26569fae113ae46a3d8a048533ef97e88fa995e9", + "shasum": "" + }, + "require": { + "php": ">=5.6.0", + "ray/aop": "~2.4", + "ray/compiler": "~1.0" + }, + "type": "library", "autoload": { "psr-4": { "Ray\\Di\\": "src/" @@ -868,7 +888,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-2-Clause" + "MIT" ], "authors": [ { @@ -877,66 +897,67 @@ } ], "description": "Guice style annotation-driven dependency injection framework", - "homepage": "https://github.com/koriym/Ray.Di", + "homepage": "https://github.com/ray-di/Ray.Di", "keywords": [ "annotations", "aop", "di", - "guice" + "guice", + "ray" ], - "time": "2015-02-28 12:55:28" + "time": "2016-10-01 19:32:57" }, { "name": "ray/web-param-module", - "version": "0.2.1", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/ray-di/Ray.WebParamModule.git", - "reference": "c7dd0fd10a2444af1df9fca42652eb955c80a1da" + "reference": "876346b1ca30ad4d0798e1cc13be6425a6b79c86" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ray-di/Ray.WebParamModule/zipball/c7dd0fd10a2444af1df9fca42652eb955c80a1da", - "reference": "c7dd0fd10a2444af1df9fca42652eb955c80a1da", + "url": "https://api.github.com/repos/ray-di/Ray.WebParamModule/zipball/876346b1ca30ad4d0798e1cc13be6425a6b79c86", + "reference": "876346b1ca30ad4d0798e1cc13be6425a6b79c86", "shasum": "" }, "require": { - "bear/resource": "~1.0@dev", - "koriym/psr4list": "~1.0@dev" + "doctrine/cache": "~1.0", + "ray/di": "~2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-develop": "0.1.x-dev" - } - }, "autoload": { "psr-4": { - "BEAR\\AppMeta\\": "src/" - } + "Ray\\WebContextParam\\": "src/" + }, + "files": [ + "doctrine_annotations.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "BEAR.Sunday application meta information", + "description": "Binds the value(s) of a web context to method parameter.", "keywords": [ - "BEARSunday" + "Ray.Di module", + "Web context", + "testability" ], - "time": "2015-02-17 11:16:56" + "time": "2015-07-15 11:23:34" }, { "name": "rize/uri-template", - "version": "0.2.6", + "version": "0.3.0", "source": { "type": "git", "url": "https://github.com/rize/UriTemplate.git", - "reference": "907ca9b59014f18f6c3f1158bdd46a3c1675e9f0" + "reference": "2496aa674438f1c48fce122ffc44291ad7014717" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rize/UriTemplate/zipball/907ca9b59014f18f6c3f1158bdd46a3c1675e9f0", - "reference": "907ca9b59014f18f6c3f1158bdd46a3c1675e9f0", + "url": "https://api.github.com/repos/rize/UriTemplate/zipball/2496aa674438f1c48fce122ffc44291ad7014717", + "reference": "2496aa674438f1c48fce122ffc44291ad7014717", "shasum": "" }, "require": { @@ -967,15 +988,13 @@ "template", "uri" ], - "time": "2014-06-11 06:00:32" + "time": "2015-04-17 16:12:22" } ], "packages-dev": [], "aliases": [], "minimum-stability": "dev", - "stability-flags": { - "bear/package": 20 - }, + "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, "platform": [], diff --git a/bear-1.0/src/Module/AppModule.php b/bear-1.0/src/Module/AppModule.php index 842e739d1..74d710832 100644 --- a/bear-1.0/src/Module/AppModule.php +++ b/bear-1.0/src/Module/AppModule.php @@ -2,7 +2,6 @@ namespace My\Hello\Module; -use BEAR\AppMeta\AppMeta; use BEAR\Package\PackageModule; use Ray\Di\AbstractModule; @@ -13,6 +12,6 @@ class AppModule extends AbstractModule */ protected function configure() { - $this->install(new PackageModule(new AppMeta('My\Hello'))); + $this->install(new PackageModule); } } diff --git a/bear-1.0/tests/AppModuleTest.php b/bear-1.0/tests/AppModuleTest.php index 8ac9ce32a..b01470c18 100644 --- a/bear-1.0/tests/AppModuleTest.php +++ b/bear-1.0/tests/AppModuleTest.php @@ -2,7 +2,6 @@ namespace My\Hello; -use BEAR\AppMeta\AppMeta; use BEAR\Package\Bootstrap; use BEAR\Sunday\Extension\Application\AbstractApp; @@ -24,7 +23,7 @@ public function contextsProvider() */ public function testNewApp($contexts) { - $app = (new Bootstrap())->newApp(new AppMeta(__NAMESPACE__), $contexts); + $app = (new Bootstrap())->getApp(__NAMESPACE__, $contexts); $this->assertInstanceOf(AbstractApp::class, $app); } } diff --git a/bear-1.0/tests/bootstrap.php b/bear-1.0/tests/bootstrap.php index f6c26eb81..4a4b3c0c8 100644 --- a/bear-1.0/tests/bootstrap.php +++ b/bear-1.0/tests/bootstrap.php @@ -1,17 +1,12 @@ addPsr4('My\Hello\\', __DIR__); AnnotationRegistry::registerLoader([$loader, 'loadClass']); // set the application path into the globals so we can access it in the tests. @@ -19,4 +14,5 @@ $_ENV['TMP_DIR'] = __DIR__ . '/tmp'; // set the resource client -$GLOBALS['RESOURCE'] = (new Injector(new AppModule, __DIR__ . '/tmp'))->getInstance(ResourceInterface::class); +$app = (new Bootstrap)->getApp('My\Hello', 'app'); +$GLOBALS['RESOURCE'] = $app->resource; diff --git a/bear-1.0/var/www/.htaccess b/bear-1.0/var/www/.htaccess deleted file mode 100644 index b8550a807..000000000 --- a/bear-1.0/var/www/.htaccess +++ /dev/null @@ -1,14 +0,0 @@ -order deny,allow - - - # turn on rewriting - RewriteEngine On - - # for all files not found in the file system, - # reroute to "index.php" bootstrap script, - # keeping the query string intact. - RewriteCond %{REQUEST_FILENAME} !-d - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !favicon.ico$ - RewriteRule ^(.*)$ /index.php [QSA,L] - diff --git a/bear-1.0/var/www/index.php b/bear-1.0/var/www/index.php index d3c4d59b5..c764fdf9c 100644 --- a/bear-1.0/var/www/index.php +++ b/bear-1.0/var/www/index.php @@ -1,4 +1,13 @@ "$ab_log" - curl "$url" > "$output" rps=`grep "Requests per second:" "$ab_log" | cut -f 7 -d " "` - m=`tail -1 "$output"` - echo "$fw: $rps: $m" >> "$results" + + # get time + count=10 + total=0 + for ((i=0; i < $count; i++)); do + curl "$url" > "$output" + t=`tail -1 "$output" | cut -f 2 -d ':'` + total=`php ./benchmarks/sum_ms.php $t $total` + done + time=`php ./benchmarks/avg_ms.php $total $count` + + # get memory and file + memory=`tail -1 "$output" | cut -f 1 -d ':'` + file=`tail -1 "$output" | cut -f 3 -d ':'` + + echo "$fw: $rps: $memory: $time: $file" >> "$results_file" + + echo "$fw" >> "$check_file" + grep "Document Length:" "$ab_log" >> "$check_file" + grep "Failed requests:" "$ab_log" >> "$check_file" + grep 'Hello World!' "$output" >> "$check_file" + echo "---" >> "$check_file" + + # check errors + touch "$error_file" + error='' + x=`grep 'Failed requests: 0' "$ab_log"` + if [ "$x" = "" ]; then + tmp=`grep "Failed requests:" "$ab_log"` + error="$error$tmp" + fi + x=`grep 'Hello World!' "$output"` + if [ "$x" = "" ]; then + tmp=`cat "$output"` + error="$error$tmp" + fi + if [ "$error" != "" ]; then + echo -e "$fw\n$error" >> "$error_file" + echo "---" >> "$error_file" + fi + + echo "$url" >> "$url_file" + echo } diff --git a/benchmarks/avg_ms.php b/benchmarks/avg_ms.php new file mode 100644 index 000000000..c90a924f0 --- /dev/null +++ b/benchmarks/avg_ms.php @@ -0,0 +1,6 @@ + $result) { - printf("|%-19s|%19s|%11s|\n", $fw, $result['rps'], $result['memory']); -} +echo build_table($results); diff --git a/cake-3.0/.htaccess b/cake-3.0/.htaccess deleted file mode 100644 index fc3aac4b2..000000000 --- a/cake-3.0/.htaccess +++ /dev/null @@ -1,5 +0,0 @@ - - RewriteEngine on - RewriteRule ^$ webroot/ [L] - RewriteRule (.*) webroot/$1 [L] - \ No newline at end of file diff --git a/cake-3.0/_benchmark/hello_world.sh b/cake-3.0/_benchmark/hello_world.sh new file mode 100644 index 000000000..f0f049590 --- /dev/null +++ b/cake-3.0/_benchmark/hello_world.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +url="$base/$fw/index.php/hello/index" diff --git a/cake-3.0/_benchmark/setup.sh b/cake-3.0/_benchmark/setup.sh new file mode 100644 index 000000000..6a4c961ca --- /dev/null +++ b/cake-3.0/_benchmark/setup.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +sudo rm -rf tmp/* +COMPOSER_PROCESS_TIMEOUT=3600 composer install --no-dev --optimize-autoloader diff --git a/cake-3.0/composer.json b/cake-3.0/composer.json index f516e0584..d7843b711 100644 --- a/cake-3.0/composer.json +++ b/cake-3.0/composer.json @@ -6,7 +6,7 @@ "license": "MIT", "require": { "php": ">=5.4.16", - "cakephp/cakephp": "~3.0", + "cakephp/cakephp": "3.0.*", "mobiledetect/mobiledetectlib": "2.*", "cakephp/migrations": "~1.0", "cakephp/plugin-installer": "*" diff --git a/cake-3.0/composer.lock b/cake-3.0/composer.lock index 9b48bfd8c..c6dd45ec9 100644 --- a/cake-3.0/composer.lock +++ b/cake-3.0/composer.lock @@ -1,10 +1,11 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "ef2254b87b67630e4f4e3b4655ece177", + "hash": "1e03e905e7bf2cca6965c7d7a42ef62a", + "content-hash": "b05bfa88894e873f7fbbc8dc7f2a6afe", "packages": [ { "name": "aura/installer-default", @@ -127,16 +128,16 @@ }, { "name": "cakephp/cakephp", - "version": "3.0.0", + "version": "3.0.14", "source": { "type": "git", "url": "https://github.com/cakephp/cakephp.git", - "reference": "09eb23891dc8134830f4e39e01a3a39f82935e41" + "reference": "3d7c826a780f6545056f351bc5aa1623b386f0c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/cakephp/zipball/09eb23891dc8134830f4e39e01a3a39f82935e41", - "reference": "09eb23891dc8134830f4e39e01a3a39f82935e41", + "url": "https://api.github.com/repos/cakephp/cakephp/zipball/3d7c826a780f6545056f351bc5aa1623b386f0c4", + "reference": "3d7c826a780f6545056f351bc5aa1623b386f0c4", "shasum": "" }, "require": { @@ -155,6 +156,7 @@ "cakephp/database": "self.version", "cakephp/datasource": "self.version", "cakephp/event": "self.version", + "cakephp/filesystem": "self.version", "cakephp/i18n": "self.version", "cakephp/log": "self.version", "cakephp/orm": "self.version", @@ -166,6 +168,11 @@ "phpunit/phpunit": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, "autoload": { "psr-4": { "Cake\\": "src" @@ -192,29 +199,29 @@ "keywords": [ "framework" ], - "time": "2015-03-22 16:42:46" + "time": "2015-09-22 02:12:36" }, { "name": "cakephp/migrations", - "version": "1.0.1", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/cakephp/migrations.git", - "reference": "3921e7f13f94c29f4ce1c7ddc4cf3745965b019a" + "reference": "6f9f3da1f872a739c7d9a1d43eeb01e258bdd2c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/migrations/zipball/3921e7f13f94c29f4ce1c7ddc4cf3745965b019a", - "reference": "3921e7f13f94c29f4ce1c7ddc4cf3745965b019a", + "url": "https://api.github.com/repos/cakephp/migrations/zipball/6f9f3da1f872a739c7d9a1d43eeb01e258bdd2c1", + "reference": "6f9f3da1f872a739c7d9a1d43eeb01e258bdd2c1", "shasum": "" }, "require": { "cakephp/cakephp": "~3.0", "php": ">=5.4", - "robmorgan/phinx": "~0.4" + "robmorgan/phinx": "dev-master" }, "require-dev": { - "cakephp/bake": "dev-master", + "cakephp/bake": "@stable", "phpunit/phpunit": "*" }, "suggest": { @@ -242,20 +249,20 @@ "cakephp", "migrations" ], - "time": "2015-03-22 18:07:33" + "time": "2015-09-30 21:58:44" }, { "name": "cakephp/plugin-installer", - "version": "0.0.11", + "version": "0.0.12", "source": { "type": "git", "url": "https://github.com/cakephp/plugin-installer.git", - "reference": "87cb0b70fc642f4a4ce5b6d8006392f01e8d0260" + "reference": "8a71f10fcc455b8b9756529433d5f0fd818b0ce1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/plugin-installer/zipball/87cb0b70fc642f4a4ce5b6d8006392f01e8d0260", - "reference": "87cb0b70fc642f4a4ce5b6d8006392f01e8d0260", + "url": "https://api.github.com/repos/cakephp/plugin-installer/zipball/8a71f10fcc455b8b9756529433d5f0fd818b0ce1", + "reference": "8a71f10fcc455b8b9756529433d5f0fd818b0ce1", "shasum": "" }, "require-dev": { @@ -282,7 +289,7 @@ } ], "description": "A composer installer for CakePHP 3.0+ plugins.", - "time": "2015-02-06 02:06:07" + "time": "2015-06-10 10:35:13" }, { "name": "ircmaxell/password-compat", @@ -328,16 +335,16 @@ }, { "name": "mobiledetect/mobiledetectlib", - "version": "2.8.12", + "version": "2.8.17", "source": { "type": "git", "url": "https://github.com/serbanghita/Mobile-Detect.git", - "reference": "af30b5b7bd683f575b1c56f74e0e0632f5dd9fc9" + "reference": "b87da5f63a76e9615a0c74fcf168657b1ea7e41d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/af30b5b7bd683f575b1c56f74e0e0632f5dd9fc9", - "reference": "af30b5b7bd683f575b1c56f74e0e0632f5dd9fc9", + "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/b87da5f63a76e9615a0c74fcf168657b1ea7e41d", + "reference": "b87da5f63a76e9615a0c74fcf168657b1ea7e41d", "shasum": "" }, "require": { @@ -378,7 +385,7 @@ "mobile detector", "php mobile detect" ], - "time": "2015-03-11 10:02:22" + "time": "2015-09-17 14:45:21" }, { "name": "nesbot/carbon", @@ -466,23 +473,23 @@ }, { "name": "robmorgan/phinx", - "version": "v0.4.3", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/robmorgan/phinx.git", - "reference": "0d1f9cb9939f65f506a8a3f5fee356764c310fd4" + "reference": "43313ad89c0ad1851aeefafc164e2822e49867e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/robmorgan/phinx/zipball/0d1f9cb9939f65f506a8a3f5fee356764c310fd4", - "reference": "0d1f9cb9939f65f506a8a3f5fee356764c310fd4", + "url": "https://api.github.com/repos/robmorgan/phinx/zipball/43313ad89c0ad1851aeefafc164e2822e49867e3", + "reference": "43313ad89c0ad1851aeefafc164e2822e49867e3", "shasum": "" }, "require": { "php": ">=5.3.2", - "symfony/config": "~2.6.0", - "symfony/console": "~2.6.0", - "symfony/yaml": "~2.6.0" + "symfony/config": "~2.7", + "symfony/console": "~2.7", + "symfony/yaml": "~2.7" }, "require-dev": { "phpunit/phpunit": "3.7.*", @@ -524,25 +531,24 @@ "migrations", "phinx" ], - "time": "2015-02-23 16:38:12" + "time": "2015-09-30 08:12:11" }, { "name": "symfony/config", - "version": "v2.6.5", - "target-dir": "Symfony/Component/Config", + "version": "v2.7.5", "source": { "type": "git", - "url": "https://github.com/symfony/Config.git", - "reference": "7a47189c7667ca69bcaafd19ef8a8941db449a2c" + "url": "https://github.com/symfony/config.git", + "reference": "9698fdf0a750d6887d5e7729d5cf099765b20e61" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Config/zipball/7a47189c7667ca69bcaafd19ef8a8941db449a2c", - "reference": "7a47189c7667ca69bcaafd19ef8a8941db449a2c", + "url": "https://api.github.com/repos/symfony/config/zipball/9698fdf0a750d6887d5e7729d5cf099765b20e61", + "reference": "9698fdf0a750d6887d5e7729d5cf099765b20e61", "shasum": "" }, "require": { - "php": ">=5.3.3", + "php": ">=5.3.9", "symfony/filesystem": "~2.3" }, "require-dev": { @@ -551,11 +557,11 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev" + "dev-master": "2.7-dev" } }, "autoload": { - "psr-0": { + "psr-4": { "Symfony\\Component\\Config\\": "" } }, @@ -564,36 +570,35 @@ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Config Component", - "homepage": "http://symfony.com", - "time": "2015-03-12 10:28:44" + "homepage": "https://symfony.com", + "time": "2015-09-21 15:02:29" }, { "name": "symfony/console", - "version": "v2.6.5", - "target-dir": "Symfony/Component/Console", + "version": "v2.7.5", "source": { "type": "git", - "url": "https://github.com/symfony/Console.git", - "reference": "53f86497ccd01677e22435cfb7262599450a90d1" + "url": "https://github.com/symfony/console.git", + "reference": "06cb17c013a82f94a3d840682b49425cd00a2161" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/53f86497ccd01677e22435cfb7262599450a90d1", - "reference": "53f86497ccd01677e22435cfb7262599450a90d1", + "url": "https://api.github.com/repos/symfony/console/zipball/06cb17c013a82f94a3d840682b49425cd00a2161", + "reference": "06cb17c013a82f94a3d840682b49425cd00a2161", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.3.9" }, "require-dev": { "psr/log": "~1.0", @@ -609,11 +614,11 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev" + "dev-master": "2.7-dev" } }, "autoload": { - "psr-0": { + "psr-4": { "Symfony\\Component\\Console\\": "" } }, @@ -622,36 +627,35 @@ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Console Component", - "homepage": "http://symfony.com", - "time": "2015-03-13 17:37:22" + "homepage": "https://symfony.com", + "time": "2015-09-25 08:32:23" }, { "name": "symfony/filesystem", - "version": "v2.6.5", - "target-dir": "Symfony/Component/Filesystem", + "version": "v2.7.5", "source": { "type": "git", - "url": "https://github.com/symfony/Filesystem.git", - "reference": "fdc5f151bc2db066b51870d5bea3773d915ced0b" + "url": "https://github.com/symfony/filesystem.git", + "reference": "a17f8a17c20e8614c15b8e116e2f4bcde102cfab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Filesystem/zipball/fdc5f151bc2db066b51870d5bea3773d915ced0b", - "reference": "fdc5f151bc2db066b51870d5bea3773d915ced0b", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/a17f8a17c20e8614c15b8e116e2f4bcde102cfab", + "reference": "a17f8a17c20e8614c15b8e116e2f4bcde102cfab", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.3.9" }, "require-dev": { "symfony/phpunit-bridge": "~2.7" @@ -659,11 +663,11 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev" + "dev-master": "2.7-dev" } }, "autoload": { - "psr-0": { + "psr-4": { "Symfony\\Component\\Filesystem\\": "" } }, @@ -672,36 +676,35 @@ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Filesystem Component", - "homepage": "http://symfony.com", - "time": "2015-03-12 10:28:44" + "homepage": "https://symfony.com", + "time": "2015-09-09 17:42:36" }, { "name": "symfony/yaml", - "version": "v2.6.5", - "target-dir": "Symfony/Component/Yaml", + "version": "v2.7.5", "source": { "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "0cd8e72071e46e15fc072270ae39ea1b66b10a9d" + "url": "https://github.com/symfony/yaml.git", + "reference": "31cb2ad0155c95b88ee55fe12bc7ff92232c1770" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/0cd8e72071e46e15fc072270ae39ea1b66b10a9d", - "reference": "0cd8e72071e46e15fc072270ae39ea1b66b10a9d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/31cb2ad0155c95b88ee55fe12bc7ff92232c1770", + "reference": "31cb2ad0155c95b88ee55fe12bc7ff92232c1770", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.3.9" }, "require-dev": { "symfony/phpunit-bridge": "~2.7" @@ -709,11 +712,11 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev" + "dev-master": "2.7-dev" } }, "autoload": { - "psr-0": { + "psr-4": { "Symfony\\Component\\Yaml\\": "" } }, @@ -722,33 +725,33 @@ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Yaml Component", - "homepage": "http://symfony.com", - "time": "2015-03-12 10:28:44" + "homepage": "https://symfony.com", + "time": "2015-09-14 14:14:09" } ], "packages-dev": [ { "name": "cakephp/bake", - "version": "1.0.1", + "version": "1.0.14", "source": { "type": "git", "url": "https://github.com/cakephp/bake.git", - "reference": "ad86dbce53c16a8c8af4ee30fdaa4ff41a924778" + "reference": "3b03927c661d5f26de2e291241ecf9600bae507b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/bake/zipball/ad86dbce53c16a8c8af4ee30fdaa4ff41a924778", - "reference": "ad86dbce53c16a8c8af4ee30fdaa4ff41a924778", + "url": "https://api.github.com/repos/cakephp/bake/zipball/3b03927c661d5f26de2e291241ecf9600bae507b", + "reference": "3b03927c661d5f26de2e291241ecf9600bae507b", "shasum": "" }, "require": { @@ -781,24 +784,25 @@ "bake", "cakephp" ], - "time": "2015-03-22 18:09:12" + "time": "2015-09-12 15:22:04" }, { "name": "cakephp/debug_kit", - "version": "3.0.1", + "version": "3.1.10", "source": { "type": "git", "url": "https://github.com/cakephp/debug_kit.git", - "reference": "824c1610fe4a79f14f0a22b56de06241dfa49c37" + "reference": "960b96b47a2f4ebd44b1d484807b80ce6e691576" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/debug_kit/zipball/824c1610fe4a79f14f0a22b56de06241dfa49c37", - "reference": "824c1610fe4a79f14f0a22b56de06241dfa49c37", + "url": "https://api.github.com/repos/cakephp/debug_kit/zipball/960b96b47a2f4ebd44b1d484807b80ce6e691576", + "reference": "960b96b47a2f4ebd44b1d484807b80ce6e691576", "shasum": "" }, "require": { - "cakephp/cakephp": "~3.0" + "cakephp/cakephp": "3.0.*", + "jdorn/sql-formatter": "~1.2" }, "require-dev": { "cakephp/cakephp-codesniffer": "dev-master", @@ -810,7 +814,7 @@ "type": "cakephp-plugin", "extra": { "branch-alias": { - "dev-master": "2.2.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -841,7 +845,7 @@ "debug", "kit" ], - "time": "2015-03-22 18:05:17" + "time": "2015-08-24 06:26:27" }, { "name": "dnoegel/php-xdg-base-dir", @@ -921,16 +925,16 @@ }, { "name": "jakub-onderka/php-console-highlighter", - "version": "v0.3.1", + "version": "v0.3.2", "source": { "type": "git", "url": "https://github.com/JakubOnderka/PHP-Console-Highlighter.git", - "reference": "05bce997da20acf873e6bf396276798f3cd2c76a" + "reference": "7daa75df45242c8d5b75a22c00a201e7954e4fb5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Highlighter/zipball/05bce997da20acf873e6bf396276798f3cd2c76a", - "reference": "05bce997da20acf873e6bf396276798f3cd2c76a", + "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Highlighter/zipball/7daa75df45242c8d5b75a22c00a201e7954e4fb5", + "reference": "7daa75df45242c8d5b75a22c00a201e7954e4fb5", "shasum": "" }, "require": { @@ -940,6 +944,7 @@ "require-dev": { "jakub-onderka/php-code-style": "~1.0", "jakub-onderka/php-parallel-lint": "~0.5", + "jakub-onderka/php-var-dump-check": "~0.1", "phpunit/phpunit": "~4.0", "squizlabs/php_codesniffer": "~1.5" }, @@ -960,20 +965,70 @@ "homepage": "http://www.acci.cz/" } ], - "time": "2014-07-14 20:59:35" + "time": "2015-04-20 18:58:01" + }, + { + "name": "jdorn/sql-formatter", + "version": "v1.2.17", + "source": { + "type": "git", + "url": "https://github.com/jdorn/sql-formatter.git", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/64990d96e0959dff8e059dfcdc1af130728d92bc", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "lib" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeremy Dorn", + "email": "jeremy@jeremydorn.com", + "homepage": "http://jeremydorn.com/" + } + ], + "description": "a PHP SQL highlighting library", + "homepage": "https://github.com/jdorn/sql-formatter/", + "keywords": [ + "highlight", + "sql" + ], + "time": "2014-01-12 16:20:24" }, { "name": "nikic/php-parser", - "version": "v1.2.0", + "version": "v1.4.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "edbf162e4cf116102d95eb635608b7d310cc01c6" + "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/edbf162e4cf116102d95eb635608b7d310cc01c6", - "reference": "edbf162e4cf116102d95eb635608b7d310cc01c6", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", + "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", "shasum": "" }, "require": { @@ -983,7 +1038,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.4-dev" } }, "autoload": { @@ -1005,28 +1060,29 @@ "parser", "php" ], - "time": "2015-03-22 16:17:25" + "time": "2015-09-19 14:15:08" }, { "name": "psy/psysh", - "version": "v0.4.3", + "version": "v0.5.2", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "b5dd7660c0ef38e96cd8cd8fa924b6be0e53906a" + "reference": "aaf8772ade08b5f0f6830774a5d5c2f800415975" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/b5dd7660c0ef38e96cd8cd8fa924b6be0e53906a", - "reference": "b5dd7660c0ef38e96cd8cd8fa924b6be0e53906a", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/aaf8772ade08b5f0f6830774a5d5c2f800415975", + "reference": "aaf8772ade08b5f0f6830774a5d5c2f800415975", "shasum": "" }, "require": { "dnoegel/php-xdg-base-dir": "0.1", "jakub-onderka/php-console-highlighter": "0.3.*", - "nikic/php-parser": "~1.0", - "php": ">=5.3.0", - "symfony/console": "~2.3.10|~2.4.2|~2.5" + "nikic/php-parser": "^1.2.1", + "php": ">=5.3.9", + "symfony/console": "~2.3.10|^2.4.2|~3.0", + "symfony/var-dumper": "~2.7|~3.0" }, "require-dev": { "fabpot/php-cs-fixer": "~1.5", @@ -1046,7 +1102,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-develop": "0.4.x-dev" + "dev-develop": "0.6.x-dev" } }, "autoload": { @@ -1076,7 +1132,66 @@ "interactive", "shell" ], - "time": "2015-03-17 15:17:33" + "time": "2015-07-16 15:26:57" + }, + { + "name": "symfony/var-dumper", + "version": "v2.7.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "ba8c9a0edf18f70a7efcb8d3eb35323a10263338" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/ba8c9a0edf18f70a7efcb8d3eb35323a10263338", + "reference": "ba8c9a0edf18f70a7efcb8d3eb35323a10263338", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7" + }, + "suggest": { + "ext-symfony_debug": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony mechanism for exploring and dumping PHP variables", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "time": "2015-09-22 14:41:01" } ], "aliases": [], diff --git a/cake-3.0/src/Console/Installer.php b/cake-3.0/src/Console/Installer.php index 1ba31fbd4..45df20377 100644 --- a/cake-3.0/src/Console/Installer.php +++ b/cake-3.0/src/Console/Installer.php @@ -41,16 +41,16 @@ public static function postInstall(Event $event) // ask if the permissions should be changed if ($io->isInteractive()) { - $validator = (function ($arg) { + $validator = function ($arg) { if (in_array($arg, ['Y', 'y', 'N', 'n'])) { return $arg; } throw new Exception('This is not a valid answer. Please choose Y or n.'); - }); + }; $setFolderPermissions = $io->askAndValidate( 'Set Folder Permissions ? (Default to Y) [Y,n]? ', $validator, - false, + 10, 'Y' ); diff --git a/cake-3.0/src/Controller/HelloController.php b/cake-3.0/src/Controller/HelloController.php index 1ef9b69a7..1064f786a 100644 --- a/cake-3.0/src/Controller/HelloController.php +++ b/cake-3.0/src/Controller/HelloController.php @@ -8,6 +8,7 @@ class HelloController extends AppController public function index() { - echo 'Hello World!'; + $this->response->body('Hello World!'); + return $this->response; } } diff --git a/cake-3.0/webroot/.htaccess b/cake-3.0/webroot/.htaccess deleted file mode 100644 index f5f2d631c..000000000 --- a/cake-3.0/webroot/.htaccess +++ /dev/null @@ -1,5 +0,0 @@ - - RewriteEngine On - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^ index.php [L] - diff --git a/cake-3.0/webroot/index.php b/cake-3.0/webroot/index.php index e17c8565b..19cef45a8 100644 --- a/cake-3.0/webroot/index.php +++ b/cake-3.0/webroot/index.php @@ -36,4 +36,4 @@ new Response() ); -echo "\n" . (memory_get_peak_usage(true)/1024/1024); +require $_SERVER['DOCUMENT_ROOT'].'/php-framework-benchmark/libs/output_data.php'; diff --git a/cake-3.1/.editorconfig b/cake-3.1/.editorconfig new file mode 100644 index 000000000..706190175 --- /dev/null +++ b/cake-3.1/.editorconfig @@ -0,0 +1,18 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.bat] +end_of_line = crlf + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/cake-3.1/.gitattributes b/cake-3.1/.gitattributes new file mode 100644 index 000000000..926a808fc --- /dev/null +++ b/cake-3.1/.gitattributes @@ -0,0 +1,36 @@ +# Define the line ending behavior of the different file extensions +# Set default behaviour, in case users don't have core.autocrlf set. +* text=auto +* text eol=lf + +# Explicitly declare text files we want to always be normalized and converted +# to native line endings on checkout. +*.php text +*.default text +*.ctp text +*.sql text +*.md text +*.po text +*.js text +*.css text +*.ini text +*.properties text +*.txt text +*.xml text +*.yml text +.htaccess text + +# Declare files that will always have CRLF line endings on checkout. +*.bat eol=crlf + +# Declare files that will always have LF line endings on checkout. +*.pem eol=lf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.gif binary +*.ico binary +*.mo binary +*.pdf binary +*.phar binary diff --git a/cake-3.1/.gitignore b/cake-3.1/.gitignore new file mode 100644 index 000000000..e2e825dcc --- /dev/null +++ b/cake-3.1/.gitignore @@ -0,0 +1,4 @@ +/vendor/* +/config/app.php +/tmp/* +/logs/* diff --git a/cake-3.1/.travis.yml b/cake-3.1/.travis.yml new file mode 100644 index 000000000..d10f74062 --- /dev/null +++ b/cake-3.1/.travis.yml @@ -0,0 +1,16 @@ +language: php + +sudo: false + +php: + - 7.0 + +before_script: + - sh -c "composer require 'cakephp/cakephp-codesniffer:dev-master'" + - phpenv rehash + +script: + - sh -c "vendor/bin/phpcs -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests ./config ./webroot" + +notifications: + email: false diff --git a/cake-3.1/README.md b/cake-3.1/README.md new file mode 100644 index 000000000..0a5d58e25 --- /dev/null +++ b/cake-3.1/README.md @@ -0,0 +1,26 @@ +# CakePHP Application Skeleton + +[![Build Status](https://api.travis-ci.org/cakephp/app.png)](https://travis-ci.org/cakephp/app) +[![License](https://poser.pugx.org/cakephp/app/license.svg)](https://packagist.org/packages/cakephp/app) + +A skeleton for creating applications with [CakePHP](http://cakephp.org) 3.x. + +The framework source code can be found here: [cakephp/cakephp](https://github.com/cakephp/cakephp). + +## Installation + +1. Download [Composer](http://getcomposer.org/doc/00-intro.md) or update `composer self-update`. +2. Run `php composer.phar create-project --prefer-dist cakephp/app [app_name]`. + +If Composer is installed globally, run +```bash +composer create-project --prefer-dist cakephp/app [app_name] +``` + +You should now be able to visit the path to where you installed the app and see +the setup traffic lights. + +## Configuration + +Read and edit `config/app.php` and setup the 'Datasources' and any other +configuration relevant for your application. diff --git a/cake-3.1/_benchmark/hello_world.sh b/cake-3.1/_benchmark/hello_world.sh new file mode 100644 index 000000000..f0f049590 --- /dev/null +++ b/cake-3.1/_benchmark/hello_world.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +url="$base/$fw/index.php/hello/index" diff --git a/cake-3.1/_benchmark/setup.sh b/cake-3.1/_benchmark/setup.sh new file mode 100644 index 000000000..566311de0 --- /dev/null +++ b/cake-3.1/_benchmark/setup.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +sudo rm -rf tmp/* +composer install --no-interaction --no-dev --optimize-autoloader diff --git a/cake-3.1/bin/cake b/cake-3.1/bin/cake new file mode 100755 index 000000000..b03438694 --- /dev/null +++ b/cake-3.1/bin/cake @@ -0,0 +1,40 @@ +#!/usr/bin/env sh +################################################################################ +# +# Cake is a shell script for invoking CakePHP shell commands +# +# CakePHP(tm) : Rapid Development Framework (http://cakephp.org) +# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +# +# Licensed under The MIT License +# For full copyright and license information, please see the LICENSE.txt +# Redistributions of files must retain the above copyright notice. +# +# @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +# @link http://cakephp.org CakePHP(tm) Project +# @since 1.2.0 +# @license http://www.opensource.org/licenses/mit-license.php MIT License +# +################################################################################ + +# Canonicalize by following every symlink of the given name recursively +canonicalize() { + NAME="$1" + if [ -f "$NAME" ] + then + DIR=$(dirname -- "$NAME") + NAME=$(cd -P "$DIR" > /dev/null && pwd -P)/$(basename -- "$NAME") + fi + while [ -h "$NAME" ]; do + DIR=$(dirname -- "$NAME") + SYM=$(readlink "$NAME") + NAME=$(cd "$DIR" > /dev/null && cd $(dirname -- "$SYM") > /dev/null && pwd)/$(basename -- "$SYM") + done + echo "$NAME" +} + +CONSOLE=$(dirname -- "$(canonicalize "$0")") +APP=$(dirname "$CONSOLE") + +exec php "$CONSOLE"/cake.php "$@" +exit diff --git a/cake-3.1/bin/cake.bat b/cake-3.1/bin/cake.bat new file mode 100644 index 000000000..4fdbd1177 --- /dev/null +++ b/cake-3.1/bin/cake.bat @@ -0,0 +1,30 @@ +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:: +:: Cake is a Windows batch script for invoking CakePHP shell commands +:: +:: CakePHP(tm) : Rapid Development Framework (http://cakephp.org) +:: Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +:: +:: Licensed under The MIT License +:: Redistributions of files must retain the above copyright notice. +:: +:: @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +:: @link http://cakephp.org CakePHP(tm) Project +:: @since 2.0.0 +:: @license http://www.opensource.org/licenses/mit-license.php MIT License +:: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +:: In order for this script to work as intended, the cake\console\ folder must be in your PATH + +@echo. +@echo off + +SET app=%0 +SET lib=%~dp0 + +php "%lib%cake.php" %* + +echo. + +exit /B %ERRORLEVEL% diff --git a/cake-3.1/bin/cake.php b/cake-3.1/bin/cake.php new file mode 100644 index 000000000..4c7d7cef8 --- /dev/null +++ b/cake-3.1/bin/cake.php @@ -0,0 +1,33 @@ +#!/usr/bin/php -q +require->php)) { + $minVersion = preg_replace('/([^0-9\.])/', '', $composer->require->php); + } +} +if (version_compare(phpversion(), $minVersion, '<')) { + fwrite(STDERR, sprintf("Minimum PHP version: %s. You are using: %s.\n", $minVersion, phpversion())); + exit(-1); +} + +include dirname(__DIR__) . '/config/bootstrap.php'; + +exit(Cake\Console\ShellDispatcher::run($argv)); diff --git a/cake-3.1/composer.json b/cake-3.1/composer.json new file mode 100644 index 000000000..b5646aa77 --- /dev/null +++ b/cake-3.1/composer.json @@ -0,0 +1,40 @@ +{ + "name": "cakephp/app", + "description": "CakePHP skeleton app", + "homepage": "http://cakephp.org", + "type": "project", + "license": "MIT", + "require": { + "php": ">=5.4.16", + "cakephp/cakephp": "3.1.*", + "mobiledetect/mobiledetectlib": "2.*", + "cakephp/migrations": "~1.0", + "cakephp/plugin-installer": "*" + }, + "require-dev": { + "psy/psysh": "@stable", + "cakephp/debug_kit": "~3.2", + "cakephp/bake": "~1.1" + }, + "suggest": { + "phpunit/phpunit": "Allows automated tests to be run without system-wide install.", + "cakephp/cakephp-codesniffer": "Allows to check the code against the coding standards used in CakePHP." + }, + "autoload": { + "psr-4": { + "App\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "App\\Test\\": "tests", + "Cake\\Test\\": "./vendor/cakephp/cakephp/tests" + } + }, + "scripts": { + "post-install-cmd": "App\\Console\\Installer::postInstall", + "post-autoload-dump": "Cake\\Composer\\Installer\\PluginInstaller::postAutoloadDump" + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/cake-3.1/composer.lock b/cake-3.1/composer.lock new file mode 100644 index 000000000..9b7d47e05 --- /dev/null +++ b/cake-3.1/composer.lock @@ -0,0 +1,1200 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "0dafa358c38deed8034fa4a5ab5d5853", + "content-hash": "05f75a961792ceab5f1eaed86e3988f2", + "packages": [ + { + "name": "aura/installer-default", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/auraphp/installer-default.git", + "reference": "52f8de3670cc1ef45a916f40f732937436d028c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/auraphp/installer-default/zipball/52f8de3670cc1ef45a916f40f732937436d028c8", + "reference": "52f8de3670cc1ef45a916f40f732937436d028c8", + "shasum": "" + }, + "type": "composer-installer", + "extra": { + "class": "Aura\\Composer\\DefaultInstaller" + }, + "autoload": { + "psr-0": { + "Aura\\Composer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Paul M. Jones", + "email": "pmjones88@gmail.com", + "homepage": "http://paul-m-jones.com" + } + ], + "description": "Installs Aura packages using the Composer defaults.", + "keywords": [ + "aura", + "installer" + ], + "time": "2012-11-26 21:35:57" + }, + { + "name": "aura/intl", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/auraphp/Aura.Intl.git", + "reference": "c5fe620167550ad6fa77dd3570fba2efc77a2a21" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/auraphp/Aura.Intl/zipball/c5fe620167550ad6fa77dd3570fba2efc77a2a21", + "reference": "c5fe620167550ad6fa77dd3570fba2efc77a2a21", + "shasum": "" + }, + "require": { + "aura/installer-default": "1.0.*", + "php": ">=5.4.0" + }, + "type": "aura-package", + "extra": { + "aura": { + "type": "library", + "config": { + "common": "Aura\\Intl\\_Config\\Common" + } + }, + "branch-alias": { + "dev-develop": "1.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Aura\\Intl": "src/" + }, + "psr-4": { + "Aura\\Intl\\_Config\\": "config/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Paul M. Jones", + "email": "pmjones88@gmail.com", + "homepage": "http://paul-m-jones.com" + }, + { + "name": "Aura.Intl Contributors", + "homepage": "https://github.com/auraphp/Aura.Intl/contributors" + }, + { + "name": "Pascal Borreli", + "email": "pascal@borreli.com" + }, + { + "name": "Mapthegod", + "email": "mapthegod@gmail.com" + }, + { + "name": "Jose Lorenzo Rodriguez", + "email": "jose.zap@gmail.com" + } + ], + "description": "The Aura.Intl package provides internationalization (I18N) tools, specifically\npackage-oriented per-locale message translation.", + "homepage": "http://auraphp.com/Aura.Intl", + "keywords": [ + "g11n", + "globalization", + "i18n", + "internationalization", + "intl", + "l10n", + "localization" + ], + "time": "2014-08-24 00:00:00" + }, + { + "name": "cakephp/cakephp", + "version": "3.1.4", + "source": { + "type": "git", + "url": "https://github.com/cakephp/cakephp.git", + "reference": "46abd7761c0528ad6eee5da6dde292abbeade851" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/cakephp/zipball/46abd7761c0528ad6eee5da6dde292abbeade851", + "reference": "46abd7761c0528ad6eee5da6dde292abbeade851", + "shasum": "" + }, + "require": { + "aura/intl": "1.1.*", + "ext-intl": "*", + "ext-mbstring": "*", + "ircmaxell/password-compat": "1.0.*", + "nesbot/carbon": "1.13.*", + "php": ">=5.4.16", + "psr/log": "1.0" + }, + "replace": { + "cakephp/cache": "self.version", + "cakephp/collection": "self.version", + "cakephp/core": "self.version", + "cakephp/database": "self.version", + "cakephp/datasource": "self.version", + "cakephp/event": "self.version", + "cakephp/filesystem": "self.version", + "cakephp/i18n": "self.version", + "cakephp/log": "self.version", + "cakephp/orm": "self.version", + "cakephp/utility": "self.version", + "cakephp/validation": "self.version" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "dev-master", + "phpunit/phpunit": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cake\\": "src" + }, + "files": [ + "src/Core/functions.php", + "src/Collection/functions.php", + "src/I18n/functions.php", + "src/Utility/bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/cakephp/graphs/contributors" + } + ], + "description": "The CakePHP framework", + "homepage": "http://cakephp.org", + "keywords": [ + "framework" + ], + "time": "2015-11-06 03:48:56" + }, + { + "name": "cakephp/migrations", + "version": "1.4", + "source": { + "type": "git", + "url": "https://github.com/cakephp/migrations.git", + "reference": "8fd5d9fddde441a7324b5f5198172215f8e804fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/migrations/zipball/8fd5d9fddde441a7324b5f5198172215f8e804fc", + "reference": "8fd5d9fddde441a7324b5f5198172215f8e804fc", + "shasum": "" + }, + "require": { + "cakephp/cakephp": "~3.1", + "php": ">=5.4", + "robmorgan/phinx": "dev-master" + }, + "require-dev": { + "cakephp/bake": "@stable", + "phpunit/phpunit": "*" + }, + "suggest": { + "cakephp/bake": "Required if you want to generate migrations." + }, + "type": "cakephp-plugin", + "autoload": { + "psr-4": { + "Migrations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/migrations/graphs/contributors" + } + ], + "description": "Database Migration plugin for CakePHP 3.0 based on Phinx", + "homepage": "https://github.com/cakephp/migrations", + "keywords": [ + "cakephp", + "migrations" + ], + "time": "2015-10-16 20:29:10" + }, + { + "name": "cakephp/plugin-installer", + "version": "0.0.12", + "source": { + "type": "git", + "url": "https://github.com/cakephp/plugin-installer.git", + "reference": "8a71f10fcc455b8b9756529433d5f0fd818b0ce1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/plugin-installer/zipball/8a71f10fcc455b8b9756529433d5f0fd818b0ce1", + "reference": "8a71f10fcc455b8b9756529433d5f0fd818b0ce1", + "shasum": "" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "dev-master", + "composer/composer": "1.0.*@dev" + }, + "type": "composer-installer", + "extra": { + "class": "Cake\\Composer\\Installer\\PluginInstaller" + }, + "autoload": { + "psr-4": { + "Cake\\Composer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "http://cakephp.org" + } + ], + "description": "A composer installer for CakePHP 3.0+ plugins.", + "time": "2015-06-10 10:35:13" + }, + { + "name": "ircmaxell/password-compat", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/ircmaxell/password_compat.git", + "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/5c5cde8822a69545767f7c7f3058cb15ff84614c", + "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "autoload": { + "files": [ + "lib/password.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anthony Ferrara", + "email": "ircmaxell@php.net", + "homepage": "http://blog.ircmaxell.com" + } + ], + "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", + "homepage": "https://github.com/ircmaxell/password_compat", + "keywords": [ + "hashing", + "password" + ], + "time": "2014-11-20 16:49:30" + }, + { + "name": "mobiledetect/mobiledetectlib", + "version": "2.8.17", + "source": { + "type": "git", + "url": "https://github.com/serbanghita/Mobile-Detect.git", + "reference": "b87da5f63a76e9615a0c74fcf168657b1ea7e41d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/b87da5f63a76e9615a0c74fcf168657b1ea7e41d", + "reference": "b87da5f63a76e9615a0c74fcf168657b1ea7e41d", + "shasum": "" + }, + "require": { + "php": ">=5.0.0" + }, + "require-dev": { + "codeclimate/php-test-reporter": "dev-master", + "johnkary/phpunit-speedtrap": "~1.0@dev", + "phpunit/phpunit": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "Mobile_Detect.php" + ], + "psr-0": { + "Detection": "namespaced/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Serban Ghita", + "email": "serbanghita@gmail.com", + "homepage": "http://mobiledetect.net", + "role": "Developer" + } + ], + "description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.", + "homepage": "https://github.com/serbanghita/Mobile-Detect", + "keywords": [ + "detect mobile devices", + "mobile", + "mobile detect", + "mobile detector", + "php mobile detect" + ], + "time": "2015-09-17 14:45:21" + }, + { + "name": "nesbot/carbon", + "version": "1.13.0", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "5cb6e71055f7b0b57956b73d324cc4de31278f42" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/5cb6e71055f7b0b57956b73d324cc4de31278f42", + "reference": "5cb6e71055f7b0b57956b73d324cc4de31278f42", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Carbon": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "http://nesbot.com" + } + ], + "description": "A simple API extension for DateTime.", + "homepage": "https://github.com/briannesbitt/Carbon", + "keywords": [ + "date", + "datetime", + "time" + ], + "time": "2014-09-26 02:52:02" + }, + { + "name": "psr/log", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Psr\\Log\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2012-12-21 11:40:51" + }, + { + "name": "robmorgan/phinx", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/robmorgan/phinx.git", + "reference": "06740769d9b02365cfbed104f9a44776a3d14478" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/robmorgan/phinx/zipball/06740769d9b02365cfbed104f9a44776a3d14478", + "reference": "06740769d9b02365cfbed104f9a44776a3d14478", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "symfony/config": "~2.7", + "symfony/console": "~2.7", + "symfony/yaml": "~2.7" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*", + "squizlabs/php_codesniffer": "dev-phpcs-fixer" + }, + "bin": [ + "bin/phinx" + ], + "type": "library", + "autoload": { + "psr-4": { + "Phinx\\": "src/Phinx" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rob Morgan", + "email": "robbym@gmail.com", + "homepage": "http://robmorgan.id.au", + "role": "Lead Developer" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com", + "homepage": "http://shadowhand.me", + "role": "Developer" + } + ], + "description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.", + "homepage": "https://phinx.org", + "keywords": [ + "database", + "database migrations", + "db", + "migrations", + "phinx" + ], + "time": "2015-11-04 21:03:56" + }, + { + "name": "symfony/config", + "version": "v2.7.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "61973327bfb054f6f470de7be033a28b76c1dc20" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/61973327bfb054f6f470de7be033a28b76c1dc20", + "reference": "61973327bfb054f6f470de7be033a28b76c1dc20", + "shasum": "" + }, + "require": { + "php": ">=5.3.9", + "symfony/filesystem": "~2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Config Component", + "homepage": "https://symfony.com", + "time": "2015-11-02 20:20:53" + }, + { + "name": "symfony/console", + "version": "v2.7.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "16bb1cb86df43c90931df65f529e7ebd79636750" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/16bb1cb86df43c90931df65f529e7ebd79636750", + "reference": "16bb1cb86df43c90931df65f529e7ebd79636750", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.1", + "symfony/process": "~2.1" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2015-11-18 09:54:26" + }, + { + "name": "symfony/filesystem", + "version": "v2.7.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "8e173509d7fdbbba3cf34d6d072f2073c0210c1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/8e173509d7fdbbba3cf34d6d072f2073c0210c1d", + "reference": "8e173509d7fdbbba3cf34d6d072f2073c0210c1d", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2015-11-18 13:41:01" + }, + { + "name": "symfony/yaml", + "version": "v2.7.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "4cfcd7a9fceba662b3c036b7d9a91f6197af046c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/4cfcd7a9fceba662b3c036b7d9a91f6197af046c", + "reference": "4cfcd7a9fceba662b3c036b7d9a91f6197af046c", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2015-11-18 13:41:01" + } + ], + "packages-dev": [ + { + "name": "cakephp/bake", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/cakephp/bake.git", + "reference": "a7d5ed3ef3022264db6d059784b1ad438c8744c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/bake/zipball/a7d5ed3ef3022264db6d059784b1ad438c8744c3", + "reference": "a7d5ed3ef3022264db6d059784b1ad438c8744c3", + "shasum": "" + }, + "require": { + "cakephp/cakephp": "3.1.*", + "php": ">=5.4" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "dev-master", + "phpunit/phpunit": "*" + }, + "type": "cakephp-plugin", + "autoload": { + "psr-4": { + "Bake\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/bake/graphs/contributors" + } + ], + "description": "Bake plugin for CakePHP 3.0", + "homepage": "https://github.com/cakephp/bake", + "keywords": [ + "bake", + "cakephp" + ], + "time": "2015-10-31 17:24:20" + }, + { + "name": "cakephp/debug_kit", + "version": "3.2.2", + "source": { + "type": "git", + "url": "https://github.com/cakephp/debug_kit.git", + "reference": "61e838edf26fc4df1becf59afb77ba10e5400924" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/debug_kit/zipball/61e838edf26fc4df1becf59afb77ba10e5400924", + "reference": "61e838edf26fc4df1becf59afb77ba10e5400924", + "shasum": "" + }, + "require": { + "cakephp/cakephp": "3.1.*", + "jdorn/sql-formatter": "~1.2" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "dev-master", + "phpunit/phpunit": "4.1.*" + }, + "suggest": { + "ext-sqlite": "DebugKit needs to store panel data in a database. SQLite is simple and easy to use." + }, + "type": "cakephp-plugin", + "autoload": { + "psr-4": { + "DebugKit\\": "src", + "DebugKit\\Test\\Fixture\\": "tests\\Fixture" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Story", + "homepage": "http://mark-story.com", + "role": "Author" + }, + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/debug_kit/graphs/contributors" + } + ], + "description": "CakePHP Debug Kit", + "homepage": "https://github.com/cakephp/debug_kit", + "keywords": [ + "cakephp", + "debug", + "kit" + ], + "time": "2015-10-21 19:57:25" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "0.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/265b8593498b997dc2d31e75b89f053b5cc9621a", + "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "@stable" + }, + "type": "project", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "time": "2014-10-24 07:27:01" + }, + { + "name": "jakub-onderka/php-console-color", + "version": "0.1", + "source": { + "type": "git", + "url": "https://github.com/JakubOnderka/PHP-Console-Color.git", + "reference": "e0b393dacf7703fc36a4efc3df1435485197e6c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Color/zipball/e0b393dacf7703fc36a4efc3df1435485197e6c1", + "reference": "e0b393dacf7703fc36a4efc3df1435485197e6c1", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "jakub-onderka/php-code-style": "1.0", + "jakub-onderka/php-parallel-lint": "0.*", + "jakub-onderka/php-var-dump-check": "0.*", + "phpunit/phpunit": "3.7.*", + "squizlabs/php_codesniffer": "1.*" + }, + "type": "library", + "autoload": { + "psr-0": { + "JakubOnderka\\PhpConsoleColor": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "jakub.onderka@gmail.com", + "homepage": "http://www.acci.cz" + } + ], + "time": "2014-04-08 15:00:19" + }, + { + "name": "jakub-onderka/php-console-highlighter", + "version": "v0.3.2", + "source": { + "type": "git", + "url": "https://github.com/JakubOnderka/PHP-Console-Highlighter.git", + "reference": "7daa75df45242c8d5b75a22c00a201e7954e4fb5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Highlighter/zipball/7daa75df45242c8d5b75a22c00a201e7954e4fb5", + "reference": "7daa75df45242c8d5b75a22c00a201e7954e4fb5", + "shasum": "" + }, + "require": { + "jakub-onderka/php-console-color": "~0.1", + "php": ">=5.3.0" + }, + "require-dev": { + "jakub-onderka/php-code-style": "~1.0", + "jakub-onderka/php-parallel-lint": "~0.5", + "jakub-onderka/php-var-dump-check": "~0.1", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~1.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JakubOnderka\\PhpConsoleHighlighter": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "acci@acci.cz", + "homepage": "http://www.acci.cz/" + } + ], + "time": "2015-04-20 18:58:01" + }, + { + "name": "jdorn/sql-formatter", + "version": "v1.2.17", + "source": { + "type": "git", + "url": "https://github.com/jdorn/sql-formatter.git", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/64990d96e0959dff8e059dfcdc1af130728d92bc", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "lib" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeremy Dorn", + "email": "jeremy@jeremydorn.com", + "homepage": "http://jeremydorn.com/" + } + ], + "description": "a PHP SQL highlighting library", + "homepage": "https://github.com/jdorn/sql-formatter/", + "keywords": [ + "highlight", + "sql" + ], + "time": "2014-01-12 16:20:24" + }, + { + "name": "nikic/php-parser", + "version": "v1.4.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", + "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "files": [ + "lib/bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2015-09-19 14:15:08" + }, + { + "name": "psy/psysh", + "version": "v0.6.1", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "0f04df0b23663799a8941fae13cd8e6299bde3ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/0f04df0b23663799a8941fae13cd8e6299bde3ed", + "reference": "0f04df0b23663799a8941fae13cd8e6299bde3ed", + "shasum": "" + }, + "require": { + "dnoegel/php-xdg-base-dir": "0.1", + "jakub-onderka/php-console-highlighter": "0.3.*", + "nikic/php-parser": "^1.2.1|~2.0", + "php": ">=5.3.9", + "symfony/console": "~2.3.10|^2.4.2|~3.0", + "symfony/var-dumper": "~2.7|~3.0" + }, + "require-dev": { + "fabpot/php-cs-fixer": "~1.5", + "phpunit/phpunit": "~3.7|~4.0|~5.0", + "squizlabs/php_codesniffer": "~2.0", + "symfony/finder": "~2.1|~3.0" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.", + "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-develop": "0.7.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psy/functions.php" + ], + "psr-4": { + "Psy\\": "src/Psy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "http://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "time": "2015-11-12 16:18:56" + }, + { + "name": "symfony/var-dumper", + "version": "v2.7.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "72bcb27411780eaee9469729aace73c0d46fb2b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/72bcb27411780eaee9469729aace73c0d46fb2b8", + "reference": "72bcb27411780eaee9469729aace73c0d46fb2b8", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "suggest": { + "ext-symfony_debug": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony mechanism for exploring and dumping PHP variables", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "time": "2015-11-18 13:41:01" + } + ], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": { + "psy/psysh": 0 + }, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": ">=5.4.16" + }, + "platform-dev": [] +} diff --git a/cake-3.1/config/app.default.php b/cake-3.1/config/app.default.php new file mode 100644 index 000000000..b40256b93 --- /dev/null +++ b/cake-3.1/config/app.default.php @@ -0,0 +1,326 @@ + true, + + /** + * Configure basic information about the application. + * + * - namespace - The namespace to find app classes under. + * - encoding - The encoding used for HTML + database connections. + * - base - The base directory the app resides in. If false this + * will be auto detected. + * - dir - Name of app directory. + * - webroot - The webroot directory. + * - wwwRoot - The file path to webroot. + * - baseUrl - To configure CakePHP to *not* use mod_rewrite and to + * use CakePHP pretty URLs, remove these .htaccess + * files: + * /.htaccess + * /webroot/.htaccess + * And uncomment the baseUrl key below. + * - fullBaseUrl - A base URL to use for absolute links. + * - imageBaseUrl - Web path to the public images directory under webroot. + * - cssBaseUrl - Web path to the public css directory under webroot. + * - jsBaseUrl - Web path to the public js directory under webroot. + * - paths - Configure paths for non class based resources. Supports the + * `plugins`, `templates`, `locales` subkeys, which allow the definition of + * paths for plugins, view templates and locale files respectively. + */ + 'App' => [ + 'namespace' => 'App', + 'encoding' => 'UTF-8', + 'base' => false, + 'dir' => 'src', + 'webroot' => 'webroot', + 'wwwRoot' => WWW_ROOT, + // 'baseUrl' => env('SCRIPT_NAME'), + 'fullBaseUrl' => false, + 'imageBaseUrl' => 'img/', + 'cssBaseUrl' => 'css/', + 'jsBaseUrl' => 'js/', + 'paths' => [ + 'plugins' => [ROOT . DS . 'plugins' . DS], + 'templates' => [APP . 'Template' . DS], + 'locales' => [APP . 'Locale' . DS], + ], + ], + + /** + * Security and encryption configuration + * + * - salt - A random string used in security hashing methods. + * The salt value is also used as the encryption key. + * You should treat it as extremely sensitive data. + */ + 'Security' => [ + 'salt' => '__SALT__', + ], + + /** + * Apply timestamps with the last modified time to static assets (js, css, images). + * Will append a querystring parameter containing the time the file was modified. + * This is useful for busting browser caches. + * + * Set to true to apply timestamps when debug is true. Set to 'force' to always + * enable timestamping regardless of debug value. + */ + 'Asset' => [ + // 'timestamp' => true, + ], + + /** + * Configure the cache adapters. + */ + 'Cache' => [ + 'default' => [ + 'className' => 'File', + 'path' => CACHE, + ], + + /** + * Configure the cache used for general framework caching. + * Translation cache files are stored with this configuration. + */ + '_cake_core_' => [ + 'className' => 'File', + 'prefix' => 'myapp_cake_core_', + 'path' => CACHE . 'persistent/', + 'serialize' => true, + 'duration' => '+2 minutes', + ], + + /** + * Configure the cache for model and datasource caches. This cache + * configuration is used to store schema descriptions, and table listings + * in connections. + */ + '_cake_model_' => [ + 'className' => 'File', + 'prefix' => 'myapp_cake_model_', + 'path' => CACHE . 'models/', + 'serialize' => true, + 'duration' => '+2 minutes', + ], + ], + + /** + * Configure the Error and Exception handlers used by your application. + * + * By default errors are displayed using Debugger, when debug is true and logged + * by Cake\Log\Log when debug is false. + * + * In CLI environments exceptions will be printed to stderr with a backtrace. + * In web environments an HTML page will be displayed for the exception. + * With debug true, framework errors like Missing Controller will be displayed. + * When debug is false, framework errors will be coerced into generic HTTP errors. + * + * Options: + * + * - `errorLevel` - int - The level of errors you are interested in capturing. + * - `trace` - boolean - Whether or not backtraces should be included in + * logged errors/exceptions. + * - `log` - boolean - Whether or not you want exceptions logged. + * - `exceptionRenderer` - string - The class responsible for rendering + * uncaught exceptions. If you choose a custom class you should place + * the file for that class in src/Error. This class needs to implement a + * render method. + * - `skipLog` - array - List of exceptions to skip for logging. Exceptions that + * extend one of the listed exceptions will also be skipped for logging. + * E.g.: + * `'skipLog' => ['Cake\Network\Exception\NotFoundException', 'Cake\Network\Exception\UnauthorizedException']` + */ + 'Error' => [ + 'errorLevel' => E_ALL & ~E_DEPRECATED, + 'exceptionRenderer' => 'Cake\Error\ExceptionRenderer', + 'skipLog' => [], + 'log' => true, + 'trace' => true, + ], + + /** + * Email configuration. + * + * By defining transports separately from delivery profiles you can easily + * re-use transport configuration across multiple profiles. + * + * You can specify multiple configurations for production, development and + * testing. + * + * Each transport needs a `className`. Valid options are as follows: + * + * Mail - Send using PHP mail function + * Smtp - Send using SMTP + * Debug - Do not send the email, just return the result + * + * You can add custom transports (or override existing transports) by adding the + * appropriate file to src/Mailer/Transport. Transports should be named + * 'YourTransport.php', where 'Your' is the name of the transport. + */ + 'EmailTransport' => [ + 'default' => [ + 'className' => 'Mail', + // The following keys are used in SMTP transports + 'host' => 'localhost', + 'port' => 25, + 'timeout' => 30, + 'username' => 'user', + 'password' => 'secret', + 'client' => null, + 'tls' => null, + ], + ], + + /** + * Email delivery profiles + * + * Delivery profiles allow you to predefine various properties about email + * messages from your application and give the settings a name. This saves + * duplication across your application and makes maintenance and development + * easier. Each profile accepts a number of keys. See `Cake\Network\Email\Email` + * for more information. + */ + 'Email' => [ + 'default' => [ + 'transport' => 'default', + 'from' => 'you@localhost', + //'charset' => 'utf-8', + //'headerCharset' => 'utf-8', + ], + ], + + /** + * Connection information used by the ORM to connect + * to your application's datastores. + * Drivers include Mysql Postgres Sqlite Sqlserver + * See vendor\cakephp\cakephp\src\Database\Driver for complete list + */ + 'Datasources' => [ + 'default' => [ + 'className' => 'Cake\Database\Connection', + 'driver' => 'Cake\Database\Driver\Mysql', + 'persistent' => false, + 'host' => 'localhost', + /** + * CakePHP will use the default DB port based on the driver selected + * MySQL on MAMP uses port 8889, MAMP users will want to uncomment + * the following line and set the port accordingly + */ + //'port' => 'nonstandard_port_number', + 'username' => 'my_app', + 'password' => 'secret', + 'database' => 'my_app', + 'encoding' => 'utf8', + 'timezone' => 'UTC', + 'cacheMetadata' => true, + 'log' => false, + + /** + * Set identifier quoting to true if you are using reserved words or + * special characters in your table or column names. Enabling this + * setting will result in queries built using the Query Builder having + * identifiers quoted when creating SQL. It should be noted that this + * decreases performance because each query needs to be traversed and + * manipulated before being executed. + */ + 'quoteIdentifiers' => false, + + /** + * During development, if using MySQL < 5.6, uncommenting the + * following line could boost the speed at which schema metadata is + * fetched from the database. It can also be set directly with the + * mysql configuration directive 'innodb_stats_on_metadata = 0' + * which is the recommended value in production environments + */ + //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'], + ], + + /** + * The test connection is used during the test suite. + */ + 'test' => [ + 'className' => 'Cake\Database\Connection', + 'driver' => 'Cake\Database\Driver\Mysql', + 'persistent' => false, + 'host' => 'localhost', + //'port' => 'nonstandard_port_number', + 'username' => 'my_app', + 'password' => 'secret', + 'database' => 'test_myapp', + 'encoding' => 'utf8', + 'timezone' => 'UTC', + 'cacheMetadata' => true, + 'quoteIdentifiers' => false, + 'log' => false, + //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'], + ], + ], + + /** + * Configures logging options + */ + 'Log' => [ + 'debug' => [ + 'className' => 'Cake\Log\Engine\FileLog', + 'path' => LOGS, + 'file' => 'debug', + 'levels' => ['notice', 'info', 'debug'], + ], + 'error' => [ + 'className' => 'Cake\Log\Engine\FileLog', + 'path' => LOGS, + 'file' => 'error', + 'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'], + ], + ], + + /** + * Session configuration. + * + * Contains an array of settings to use for session configuration. The + * `defaults` key is used to define a default preset to use for sessions, any + * settings declared here will override the settings of the default config. + * + * ## Options + * + * - `cookie` - The name of the cookie to use. Defaults to 'CAKEPHP'. + * - `cookiePath` - The url path for which session cookie is set. Maps to the + * `session.cookie_path` php.ini config. Defaults to base path of app. + * - `timeout` - The time in minutes the session should be valid for. + * Pass 0 to disable checking timeout. + * Please note that php.ini's session.gc_maxlifetime must be equal to or greater + * than the largest Session['timeout'] in all served websites for it to have the + * desired effect. + * - `defaults` - The default configuration set to use as a basis for your session. + * There are four built-in options: php, cake, cache, database. + * - `handler` - Can be used to enable a custom session handler. Expects an + * array with at least the `engine` key, being the name of the Session engine + * class to use for managing the session. CakePHP bundles the `CacheSession` + * and `DatabaseSession` engines. + * - `ini` - An associative array of additional ini values to set. + * + * The built-in `defaults` options are: + * + * - 'php' - Uses settings defined in your php.ini. + * - 'cake' - Saves session files in CakePHP's /tmp directory. + * - 'database' - Uses CakePHP's database sessions. + * - 'cache' - Use the Cache class to save sessions. + * + * To define a custom session handler, save it at src/Network/Session/.php. + * Make sure the class implements PHP's `SessionHandlerInterface` and set + * Session.handler to + * + * To use database sessions, load the SQL file located at config/Schema/sessions.sql + */ + 'Session' => [ + 'defaults' => 'php', + ], +]; diff --git a/cake-3.1/config/app.php b/cake-3.1/config/app.php new file mode 100644 index 000000000..fa8d7e2b6 --- /dev/null +++ b/cake-3.1/config/app.php @@ -0,0 +1,326 @@ + false, + + /** + * Configure basic information about the application. + * + * - namespace - The namespace to find app classes under. + * - encoding - The encoding used for HTML + database connections. + * - base - The base directory the app resides in. If false this + * will be auto detected. + * - dir - Name of app directory. + * - webroot - The webroot directory. + * - wwwRoot - The file path to webroot. + * - baseUrl - To configure CakePHP to *not* use mod_rewrite and to + * use CakePHP pretty URLs, remove these .htaccess + * files: + * /.htaccess + * /webroot/.htaccess + * And uncomment the baseUrl key below. + * - fullBaseUrl - A base URL to use for absolute links. + * - imageBaseUrl - Web path to the public images directory under webroot. + * - cssBaseUrl - Web path to the public css directory under webroot. + * - jsBaseUrl - Web path to the public js directory under webroot. + * - paths - Configure paths for non class based resources. Supports the + * `plugins`, `templates`, `locales` subkeys, which allow the definition of + * paths for plugins, view templates and locale files respectively. + */ + 'App' => [ + 'namespace' => 'App', + 'encoding' => 'UTF-8', + 'base' => false, + 'dir' => 'src', + 'webroot' => 'webroot', + 'wwwRoot' => WWW_ROOT, + // 'baseUrl' => env('SCRIPT_NAME'), + 'fullBaseUrl' => false, + 'imageBaseUrl' => 'img/', + 'cssBaseUrl' => 'css/', + 'jsBaseUrl' => 'js/', + 'paths' => [ + 'plugins' => [ROOT . DS . 'plugins' . DS], + 'templates' => [APP . 'Template' . DS], + 'locales' => [APP . 'Locale' . DS], + ], + ], + + /** + * Security and encryption configuration + * + * - salt - A random string used in security hashing methods. + * The salt value is also used as the encryption key. + * You should treat it as extremely sensitive data. + */ + 'Security' => [ + 'salt' => '6e9e8d932631bf1c519235db5eaa542631092f6e6febf52ec47a3c427f051a84', + ], + + /** + * Apply timestamps with the last modified time to static assets (js, css, images). + * Will append a querystring parameter containing the time the file was modified. + * This is useful for busting browser caches. + * + * Set to true to apply timestamps when debug is true. Set to 'force' to always + * enable timestamping regardless of debug value. + */ + 'Asset' => [ + // 'timestamp' => true, + ], + + /** + * Configure the cache adapters. + */ + 'Cache' => [ + 'default' => [ + 'className' => 'File', + 'path' => CACHE, + ], + + /** + * Configure the cache used for general framework caching. + * Translation cache files are stored with this configuration. + */ + '_cake_core_' => [ + 'className' => 'File', + 'prefix' => 'myapp_cake_core_', + 'path' => CACHE . 'persistent/', + 'serialize' => true, + 'duration' => '+2 minutes', + ], + + /** + * Configure the cache for model and datasource caches. This cache + * configuration is used to store schema descriptions, and table listings + * in connections. + */ + '_cake_model_' => [ + 'className' => 'File', + 'prefix' => 'myapp_cake_model_', + 'path' => CACHE . 'models/', + 'serialize' => true, + 'duration' => '+2 minutes', + ], + ], + + /** + * Configure the Error and Exception handlers used by your application. + * + * By default errors are displayed using Debugger, when debug is true and logged + * by Cake\Log\Log when debug is false. + * + * In CLI environments exceptions will be printed to stderr with a backtrace. + * In web environments an HTML page will be displayed for the exception. + * With debug true, framework errors like Missing Controller will be displayed. + * When debug is false, framework errors will be coerced into generic HTTP errors. + * + * Options: + * + * - `errorLevel` - int - The level of errors you are interested in capturing. + * - `trace` - boolean - Whether or not backtraces should be included in + * logged errors/exceptions. + * - `log` - boolean - Whether or not you want exceptions logged. + * - `exceptionRenderer` - string - The class responsible for rendering + * uncaught exceptions. If you choose a custom class you should place + * the file for that class in src/Error. This class needs to implement a + * render method. + * - `skipLog` - array - List of exceptions to skip for logging. Exceptions that + * extend one of the listed exceptions will also be skipped for logging. + * E.g.: + * `'skipLog' => ['Cake\Network\Exception\NotFoundException', 'Cake\Network\Exception\UnauthorizedException']` + */ + 'Error' => [ + 'errorLevel' => E_ALL & ~E_DEPRECATED, + 'exceptionRenderer' => 'Cake\Error\ExceptionRenderer', + 'skipLog' => [], + 'log' => true, + 'trace' => true, + ], + + /** + * Email configuration. + * + * By defining transports separately from delivery profiles you can easily + * re-use transport configuration across multiple profiles. + * + * You can specify multiple configurations for production, development and + * testing. + * + * Each transport needs a `className`. Valid options are as follows: + * + * Mail - Send using PHP mail function + * Smtp - Send using SMTP + * Debug - Do not send the email, just return the result + * + * You can add custom transports (or override existing transports) by adding the + * appropriate file to src/Mailer/Transport. Transports should be named + * 'YourTransport.php', where 'Your' is the name of the transport. + */ + 'EmailTransport' => [ + 'default' => [ + 'className' => 'Mail', + // The following keys are used in SMTP transports + 'host' => 'localhost', + 'port' => 25, + 'timeout' => 30, + 'username' => 'user', + 'password' => 'secret', + 'client' => null, + 'tls' => null, + ], + ], + + /** + * Email delivery profiles + * + * Delivery profiles allow you to predefine various properties about email + * messages from your application and give the settings a name. This saves + * duplication across your application and makes maintenance and development + * easier. Each profile accepts a number of keys. See `Cake\Network\Email\Email` + * for more information. + */ + 'Email' => [ + 'default' => [ + 'transport' => 'default', + 'from' => 'you@localhost', + //'charset' => 'utf-8', + //'headerCharset' => 'utf-8', + ], + ], + + /** + * Connection information used by the ORM to connect + * to your application's datastores. + * Drivers include Mysql Postgres Sqlite Sqlserver + * See vendor\cakephp\cakephp\src\Database\Driver for complete list + */ + 'Datasources' => [ + 'default' => [ + 'className' => 'Cake\Database\Connection', + 'driver' => 'Cake\Database\Driver\Mysql', + 'persistent' => false, + 'host' => 'localhost', + /** + * CakePHP will use the default DB port based on the driver selected + * MySQL on MAMP uses port 8889, MAMP users will want to uncomment + * the following line and set the port accordingly + */ + //'port' => 'nonstandard_port_number', + 'username' => 'my_app', + 'password' => 'secret', + 'database' => 'my_app', + 'encoding' => 'utf8', + 'timezone' => 'UTC', + 'cacheMetadata' => true, + 'log' => false, + + /** + * Set identifier quoting to true if you are using reserved words or + * special characters in your table or column names. Enabling this + * setting will result in queries built using the Query Builder having + * identifiers quoted when creating SQL. It should be noted that this + * decreases performance because each query needs to be traversed and + * manipulated before being executed. + */ + 'quoteIdentifiers' => false, + + /** + * During development, if using MySQL < 5.6, uncommenting the + * following line could boost the speed at which schema metadata is + * fetched from the database. It can also be set directly with the + * mysql configuration directive 'innodb_stats_on_metadata = 0' + * which is the recommended value in production environments + */ + //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'], + ], + + /** + * The test connection is used during the test suite. + */ + 'test' => [ + 'className' => 'Cake\Database\Connection', + 'driver' => 'Cake\Database\Driver\Mysql', + 'persistent' => false, + 'host' => 'localhost', + //'port' => 'nonstandard_port_number', + 'username' => 'my_app', + 'password' => 'secret', + 'database' => 'test_myapp', + 'encoding' => 'utf8', + 'timezone' => 'UTC', + 'cacheMetadata' => true, + 'quoteIdentifiers' => false, + 'log' => false, + //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'], + ], + ], + + /** + * Configures logging options + */ + 'Log' => [ + 'debug' => [ + 'className' => 'Cake\Log\Engine\FileLog', + 'path' => LOGS, + 'file' => 'debug', + 'levels' => ['notice', 'info', 'debug'], + ], + 'error' => [ + 'className' => 'Cake\Log\Engine\FileLog', + 'path' => LOGS, + 'file' => 'error', + 'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'], + ], + ], + + /** + * Session configuration. + * + * Contains an array of settings to use for session configuration. The + * `defaults` key is used to define a default preset to use for sessions, any + * settings declared here will override the settings of the default config. + * + * ## Options + * + * - `cookie` - The name of the cookie to use. Defaults to 'CAKEPHP'. + * - `cookiePath` - The url path for which session cookie is set. Maps to the + * `session.cookie_path` php.ini config. Defaults to base path of app. + * - `timeout` - The time in minutes the session should be valid for. + * Pass 0 to disable checking timeout. + * Please note that php.ini's session.gc_maxlifetime must be equal to or greater + * than the largest Session['timeout'] in all served websites for it to have the + * desired effect. + * - `defaults` - The default configuration set to use as a basis for your session. + * There are four built-in options: php, cake, cache, database. + * - `handler` - Can be used to enable a custom session handler. Expects an + * array with at least the `engine` key, being the name of the Session engine + * class to use for managing the session. CakePHP bundles the `CacheSession` + * and `DatabaseSession` engines. + * - `ini` - An associative array of additional ini values to set. + * + * The built-in `defaults` options are: + * + * - 'php' - Uses settings defined in your php.ini. + * - 'cake' - Saves session files in CakePHP's /tmp directory. + * - 'database' - Uses CakePHP's database sessions. + * - 'cache' - Use the Cache class to save sessions. + * + * To define a custom session handler, save it at src/Network/Session/.php. + * Make sure the class implements PHP's `SessionHandlerInterface` and set + * Session.handler to + * + * To use database sessions, load the SQL file located at config/Schema/sessions.sql + */ + 'Session' => [ + 'defaults' => 'php', + ], +]; diff --git a/cake-3.1/config/bootstrap.php b/cake-3.1/config/bootstrap.php new file mode 100644 index 000000000..178edaa58 --- /dev/null +++ b/cake-3.1/config/bootstrap.php @@ -0,0 +1,203 @@ +getMessage() . "\n"); +} + +// Load an environment local configuration file. +// You can use a file like app_local.php to provide local overrides to your +// shared configuration. +//Configure::load('app_local', 'default'); + +// When debug = false the metadata cache should last +// for a very very long time, as we don't want +// to refresh the cache while users are doing requests. +if (!Configure::read('debug')) { + Configure::write('Cache._cake_model_.duration', '+1 years'); + Configure::write('Cache._cake_core_.duration', '+1 years'); +} + +/** + * Set server timezone to UTC. You can change it to another timezone of your + * choice but using UTC makes time calculations / conversions easier. + */ +date_default_timezone_set('UTC'); + +/** + * Configure the mbstring extension to use the correct encoding. + */ +mb_internal_encoding(Configure::read('App.encoding')); + +/** + * Set the default locale. This controls how dates, number and currency is + * formatted and sets the default language to use for translations. + */ +ini_set('intl.default_locale', 'en_US'); + +/** + * Register application error and exception handlers. + */ +$isCli = php_sapi_name() === 'cli'; +if ($isCli) { + (new ConsoleErrorHandler(Configure::read('Error')))->register(); +} else { + (new ErrorHandler(Configure::read('Error')))->register(); +} + +// Include the CLI bootstrap overrides. +if ($isCli) { + require __DIR__ . '/bootstrap_cli.php'; +} + +/** + * Set the full base URL. + * This URL is used as the base of all absolute links. + * + * If you define fullBaseUrl in your config file you can remove this. + */ +if (!Configure::read('App.fullBaseUrl')) { + $s = null; + if (env('HTTPS')) { + $s = 's'; + } + + $httpHost = env('HTTP_HOST'); + if (isset($httpHost)) { + Configure::write('App.fullBaseUrl', 'http' . $s . '://' . $httpHost); + } + unset($httpHost, $s); +} + +Cache::config(Configure::consume('Cache')); +ConnectionManager::config(Configure::consume('Datasources')); +Email::configTransport(Configure::consume('EmailTransport')); +Email::config(Configure::consume('Email')); +Log::config(Configure::consume('Log')); +Security::salt(Configure::consume('Security.salt')); + +/** + * The default crypto extension in 3.0 is OpenSSL. + * If you are migrating from 2.x uncomment this code to + * use a more compatible Mcrypt based implementation + */ +// Security::engine(new \Cake\Utility\Crypto\Mcrypt()); + +/** + * Setup detectors for mobile and tablet. + */ +Request::addDetector('mobile', function ($request) { + $detector = new \Detection\MobileDetect(); + return $detector->isMobile(); +}); +Request::addDetector('tablet', function ($request) { + $detector = new \Detection\MobileDetect(); + return $detector->isTablet(); +}); + +/** + * Custom Inflector rules, can be set to correctly pluralize or singularize + * table, model, controller names or whatever other string is passed to the + * inflection functions. + * + * Inflector::rules('plural', ['/^(inflect)or$/i' => '\1ables']); + * Inflector::rules('irregular', ['red' => 'redlings']); + * Inflector::rules('uninflected', ['dontinflectme']); + * Inflector::rules('transliteration', ['/Ã¥/' => 'aa']); + */ + +/** + * Plugins need to be loaded manually, you can either load them one by one or all of them in a single call + * Uncomment one of the lines below, as you need. make sure you read the documentation on Plugin to use more + * advanced ways of loading plugins + * + * Plugin::loadAll(); // Loads all plugins at once + * Plugin::load('Migrations'); //Loads a single plugin named Migrations + * + */ + +Plugin::load('Migrations'); + +// Only try to load DebugKit in development mode +// Debug Kit should not be installed on a production system +if (Configure::read('debug')) { + Plugin::load('DebugKit', ['bootstrap' => true]); +} + +/** + * Connect middleware/dispatcher filters. + */ +DispatcherFactory::add('Asset'); +DispatcherFactory::add('Routing'); +DispatcherFactory::add('ControllerFactory'); + +/** + * Enable default locale format parsing. + * This is needed for matching the auto-localized string output of Time() class when parsing dates. + */ +Type::build('date')->useLocaleParser(); +Type::build('datetime')->useLocaleParser(); diff --git a/cake-3.1/config/bootstrap_cli.php b/cake-3.1/config/bootstrap_cli.php new file mode 100644 index 000000000..13cf979fa --- /dev/null +++ b/cake-3.1/config/bootstrap_cli.php @@ -0,0 +1,32 @@ +connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']); + + /** + * ...and connect the rest of 'Pages' controller's URLs. + */ + $routes->connect('/pages/*', ['controller' => 'Pages', 'action' => 'display']); + + /** + * Connect catchall routes for all controllers. + * + * Using the argument `DashedRoute`, the `fallbacks` method is a shortcut for + * `$routes->connect('/:controller', ['action' => 'index'], ['routeClass' => 'DashedRoute']);` + * `$routes->connect('/:controller/:action/*', [], ['routeClass' => 'DashedRoute']);` + * + * Any route class can be used with this method, such as: + * - DashedRoute + * - InflectedRoute + * - Route + * - Or your own route class + * + * You can remove these routes once you've connected the + * routes you want in your application. + */ + $routes->fallbacks('DashedRoute'); +}); + +/** + * Load all plugin routes. See the Plugin documentation on + * how to customize the loading of plugin routes. + */ +Plugin::routes(); diff --git a/cake-3.1/config/schema/i18n.sql b/cake-3.1/config/schema/i18n.sql new file mode 100644 index 000000000..66a42bd19 --- /dev/null +++ b/cake-3.1/config/schema/i18n.sql @@ -0,0 +1,27 @@ +# $Id$ +# +# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +# +# Licensed under The MIT License +# For full copyright and license information, please see the LICENSE.txt +# Redistributions of files must retain the above copyright notice. +# MIT License (http://www.opensource.org/licenses/mit-license.php) + +CREATE TABLE i18n ( + id int(10) NOT NULL auto_increment, + locale varchar(6) NOT NULL, + model varchar(255) NOT NULL, + foreign_key int(10) NOT NULL, + field varchar(255) NOT NULL, + content mediumtext, + PRIMARY KEY (id), +# UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field), +# INDEX I18N_LOCALE_ROW(locale, model, foreign_key), +# INDEX I18N_LOCALE_MODEL(locale, model), +# INDEX I18N_FIELD(model, foreign_key, field), +# INDEX I18N_ROW(model, foreign_key), + INDEX locale (locale), + INDEX model (model), + INDEX row_id (foreign_key), + INDEX field (field) +); \ No newline at end of file diff --git a/cake-3.1/config/schema/sessions.sql b/cake-3.1/config/schema/sessions.sql new file mode 100644 index 000000000..6fa816cbe --- /dev/null +++ b/cake-3.1/config/schema/sessions.sql @@ -0,0 +1,15 @@ +# $Id$ +# +# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +# +# Licensed under The MIT License +# For full copyright and license information, please see the LICENSE.txt +# Redistributions of files must retain the above copyright notice. +# MIT License (http://www.opensource.org/licenses/mit-license.php) + +CREATE TABLE sessions ( + id varchar(40) NOT NULL default '', + data text, + expires INT(11) NOT NULL, + PRIMARY KEY (id) +); diff --git a/cake-3.1/index.php b/cake-3.1/index.php new file mode 100644 index 000000000..fc5e39ccb --- /dev/null +++ b/cake-3.1/index.php @@ -0,0 +1,16 @@ + + + + + + + + + + + ./tests/TestCase + + + + + + + + + + + + + + + + + ./vendor/ + + + diff --git a/fuel-1.8-dev/fuel/app/migrations/.gitkeep b/cake-3.1/plugins/empty similarity index 100% rename from fuel-1.8-dev/fuel/app/migrations/.gitkeep rename to cake-3.1/plugins/empty diff --git a/cake-3.1/src/Console/Installer.php b/cake-3.1/src/Console/Installer.php new file mode 100644 index 000000000..06d1115f7 --- /dev/null +++ b/cake-3.1/src/Console/Installer.php @@ -0,0 +1,192 @@ +getIO(); + + $rootDir = dirname(dirname(__DIR__)); + + static::createAppConfig($rootDir, $io); + static::createWritableDirectories($rootDir, $io); + + // ask if the permissions should be changed + if ($io->isInteractive()) { + $validator = function ($arg) { + if (in_array($arg, ['Y', 'y', 'N', 'n'])) { + return $arg; + } + throw new Exception('This is not a valid answer. Please choose Y or n.'); + }; + $setFolderPermissions = $io->askAndValidate( + 'Set Folder Permissions ? (Default to Y) [Y,n]? ', + $validator, + 10, + 'Y' + ); + + if (in_array($setFolderPermissions, ['Y', 'y'])) { + static::setFolderPermissions($rootDir, $io); + } + } else { + static::setFolderPermissions($rootDir, $io); + } + + static::setSecuritySalt($rootDir, $io); + + if (class_exists('\Cake\Codeception\Console\Installer')) { + \Cake\Codeception\Console\Installer::customizeCodeceptionBinary($event); + } + } + + /** + * Create the config/app.php file if it does not exist. + * + * @param string $dir The application's root directory. + * @param \Composer\IO\IOInterface $io IO interface to write to console. + * @return void + */ + public static function createAppConfig($dir, $io) + { + $appConfig = $dir . '/config/app.php'; + $defaultConfig = $dir . '/config/app.default.php'; + if (!file_exists($appConfig)) { + copy($defaultConfig, $appConfig); + $io->write('Created `config/app.php` file'); + } + } + + /** + * Create the `logs` and `tmp` directories. + * + * @param string $dir The application's root directory. + * @param \Composer\IO\IOInterface $io IO interface to write to console. + * @return void + */ + public static function createWritableDirectories($dir, $io) + { + $paths = [ + 'logs', + 'tmp', + 'tmp/cache', + 'tmp/cache/models', + 'tmp/cache/persistent', + 'tmp/cache/views', + 'tmp/sessions', + 'tmp/tests' + ]; + + foreach ($paths as $path) { + $path = $dir . '/' . $path; + if (!file_exists($path)) { + mkdir($path); + $io->write('Created `' . $path . '` directory'); + } + } + } + + /** + * Set globally writable permissions on the "tmp" and "logs" directory. + * + * This is not the most secure default, but it gets people up and running quickly. + * + * @param string $dir The application's root directory. + * @param \Composer\IO\IOInterface $io IO interface to write to console. + * @return void + */ + public static function setFolderPermissions($dir, $io) + { + // Change the permissions on a path and output the results. + $changePerms = function ($path, $perms, $io) { + // Get current permissions in decimal format so we can bitmask it. + $currentPerms = octdec(substr(sprintf('%o', fileperms($path)), -4)); + if (($currentPerms & $perms) == $perms) { + return; + } + + $res = chmod($path, $currentPerms | $perms); + if ($res) { + $io->write('Permissions set on ' . $path); + } else { + $io->write('Failed to set permissions on ' . $path); + } + }; + + $walker = function ($dir, $perms, $io) use (&$walker, $changePerms) { + $files = array_diff(scandir($dir), ['.', '..']); + foreach ($files as $file) { + $path = $dir . '/' . $file; + + if (!is_dir($path)) { + continue; + } + + $changePerms($path, $perms, $io); + $walker($path, $perms, $io); + } + }; + + $worldWritable = bindec('0000000111'); + $walker($dir . '/tmp', $worldWritable, $io); + $changePerms($dir . '/tmp', $worldWritable, $io); + $changePerms($dir . '/logs', $worldWritable, $io); + } + + /** + * Set the security.salt value in the application's config file. + * + * @param string $dir The application's root directory. + * @param \Composer\IO\IOInterface $io IO interface to write to console. + * @return void + */ + public static function setSecuritySalt($dir, $io) + { + $config = $dir . '/config/app.php'; + $content = file_get_contents($config); + + $newKey = hash('sha256', $dir . php_uname() . microtime(true)); + $content = str_replace('__SALT__', $newKey, $content, $count); + + if ($count == 0) { + $io->write('No Security.salt placeholder to replace.'); + return; + } + + $result = file_put_contents($config, $content); + if ($result) { + $io->write('Updated Security.salt value in config/app.php'); + return; + } + $io->write('Unable to update Security.salt value.'); + } +} diff --git a/cake-3.1/src/Controller/AppController.php b/cake-3.1/src/Controller/AppController.php new file mode 100644 index 000000000..ab5190742 --- /dev/null +++ b/cake-3.1/src/Controller/AppController.php @@ -0,0 +1,62 @@ +loadComponent('Security');` + * + * @return void + */ + public function initialize() + { + parent::initialize(); + + $this->loadComponent('RequestHandler'); + $this->loadComponent('Flash'); + } + + /** + * Before render callback. + * + * @param \Cake\Event\Event $event The beforeRender event. + * @return void + */ + public function beforeRender(Event $event) + { + if (!array_key_exists('_serialize', $this->viewVars) && + in_array($this->response->type(), ['application/json', 'application/xml']) + ) { + $this->set('_serialize', true); + } + } +} diff --git a/fuel-1.8-dev/fuel/app/modules/.gitkeep b/cake-3.1/src/Controller/Component/empty similarity index 100% rename from fuel-1.8-dev/fuel/app/modules/.gitkeep rename to cake-3.1/src/Controller/Component/empty diff --git a/cake-3.1/src/Controller/HelloController.php b/cake-3.1/src/Controller/HelloController.php new file mode 100644 index 000000000..1064f786a --- /dev/null +++ b/cake-3.1/src/Controller/HelloController.php @@ -0,0 +1,14 @@ +response->body('Hello World!'); + return $this->response; + } +} diff --git a/cake-3.1/src/Controller/PagesController.php b/cake-3.1/src/Controller/PagesController.php new file mode 100644 index 000000000..ae7e93eee --- /dev/null +++ b/cake-3.1/src/Controller/PagesController.php @@ -0,0 +1,65 @@ +redirect('/'); + } + $page = $subpage = null; + + if (!empty($path[0])) { + $page = $path[0]; + } + if (!empty($path[1])) { + $subpage = $path[1]; + } + $this->set(compact('page', 'subpage')); + + try { + $this->render(implode('/', $path)); + } catch (MissingTemplateException $e) { + if (Configure::read('debug')) { + throw $e; + } + throw new NotFoundException(); + } + } +} diff --git a/fuel-1.8-dev/fuel/app/tests/controller/.gitkeep b/cake-3.1/src/Model/Behavior/empty similarity index 100% rename from fuel-1.8-dev/fuel/app/tests/controller/.gitkeep rename to cake-3.1/src/Model/Behavior/empty diff --git a/fuel-1.8-dev/fuel/app/tests/model/.gitkeep b/cake-3.1/src/Model/Entity/empty similarity index 100% rename from fuel-1.8-dev/fuel/app/tests/model/.gitkeep rename to cake-3.1/src/Model/Entity/empty diff --git a/fuel-1.8-dev/fuel/app/tests/view/.gitkeep b/cake-3.1/src/Model/Table/empty similarity index 100% rename from fuel-1.8-dev/fuel/app/tests/view/.gitkeep rename to cake-3.1/src/Model/Table/empty diff --git a/cake-3.1/src/Shell/ConsoleShell.php b/cake-3.1/src/Shell/ConsoleShell.php new file mode 100644 index 000000000..742c876ea --- /dev/null +++ b/cake-3.1/src/Shell/ConsoleShell.php @@ -0,0 +1,79 @@ +err('Unable to load Psy\Shell.'); + $this->err(''); + $this->err('Make sure you have installed psysh as a dependency,'); + $this->err('and that Psy\Shell is registered in your autoloader.'); + $this->err(''); + $this->err('If you are using composer run'); + $this->err(''); + $this->err('$ php composer.phar require --dev psy/psysh'); + $this->err(''); + return 1; + } + + $this->out("You can exit with `CTRL-C` or `exit`"); + $this->out(''); + + Log::drop('debug'); + Log::drop('error'); + $this->_io->setLoggers(false); + restore_error_handler(); + restore_exception_handler(); + + $psy = new PsyShell(); + $psy->run(); + } + + /** + * Display help for this console. + * + * @return ConsoleOptionParser + */ + public function getOptionParser() + { + $parser = new ConsoleOptionParser('console'); + $parser->description( + 'This shell provides a REPL that you can use to interact ' . + 'with your application in an interactive fashion. You can use ' . + 'it to run adhoc queries with your models, or experiment ' . + 'and explore the features of CakePHP and your application.' . + "\n\n" . + 'You will need to have psysh installed for this Shell to work.' + ); + return $parser; + } +} diff --git a/cake-3.1/src/Template/Element/Flash/default.ctp b/cake-3.1/src/Template/Element/Flash/default.ctp new file mode 100644 index 000000000..bc1e2c369 --- /dev/null +++ b/cake-3.1/src/Template/Element/Flash/default.ctp @@ -0,0 +1,7 @@ + +
diff --git a/cake-3.1/src/Template/Element/Flash/error.ctp b/cake-3.1/src/Template/Element/Flash/error.ctp new file mode 100644 index 000000000..e0f138689 --- /dev/null +++ b/cake-3.1/src/Template/Element/Flash/error.ctp @@ -0,0 +1 @@ +
diff --git a/cake-3.1/src/Template/Element/Flash/success.ctp b/cake-3.1/src/Template/Element/Flash/success.ctp new file mode 100644 index 000000000..b9c212b75 --- /dev/null +++ b/cake-3.1/src/Template/Element/Flash/success.ctp @@ -0,0 +1 @@ +
diff --git a/cake-3.1/src/Template/Email/html/default.ctp b/cake-3.1/src/Template/Email/html/default.ctp new file mode 100644 index 000000000..386674a1a --- /dev/null +++ b/cake-3.1/src/Template/Email/html/default.ctp @@ -0,0 +1,22 @@ + + ' . $line . "

\n"; +endforeach; +?> diff --git a/cake-3.1/src/Template/Email/text/default.ctp b/cake-3.1/src/Template/Email/text/default.ctp new file mode 100644 index 000000000..704b46f03 --- /dev/null +++ b/cake-3.1/src/Template/Email/text/default.ctp @@ -0,0 +1,16 @@ + + diff --git a/cake-3.1/src/Template/Error/error400.ctp b/cake-3.1/src/Template/Error/error400.ctp new file mode 100644 index 000000000..5cd7ce6ee --- /dev/null +++ b/cake-3.1/src/Template/Error/error400.ctp @@ -0,0 +1,38 @@ +layout = 'dev_error'; + + $this->assign('title', $message); + $this->assign('templateName', 'error400.ctp'); + + $this->start('file'); +?> +queryString)) : ?> +

+ SQL Query: + queryString) ?> +

+ +params)) : ?> + SQL Query Params: + params) ?> + +element('auto_table_warning') ?> +end(); +endif; +?> +

+

+ : + '{$url}'" + ) ?> +

diff --git a/cake-3.1/src/Template/Error/error500.ctp b/cake-3.1/src/Template/Error/error500.ctp new file mode 100644 index 000000000..d2b7d92a1 --- /dev/null +++ b/cake-3.1/src/Template/Error/error500.ctp @@ -0,0 +1,37 @@ +layout = 'dev_error'; + + $this->assign('title', $message); + $this->assign('templateName', 'error500.ctp'); + + $this->start('file'); +?> +queryString)) : ?> +

+ SQL Query: + queryString) ?> +

+ +params)) : ?> + SQL Query Params: + params) ?> + +element('auto_table_warning'); + + if (extension_loaded('xdebug')): + xdebug_print_function_stack(); + endif; + + $this->end(); +endif; +?> +

+

+ : + +

diff --git a/cake-3.1/src/Template/Layout/Email/html/default.ctp b/cake-3.1/src/Template/Layout/Email/html/default.ctp new file mode 100644 index 000000000..2b4397008 --- /dev/null +++ b/cake-3.1/src/Template/Layout/Email/html/default.ctp @@ -0,0 +1,24 @@ + + + + + <?= $this->fetch('title') ?> + + + fetch('content') ?> + + diff --git a/cake-3.1/src/Template/Layout/Email/text/default.ctp b/cake-3.1/src/Template/Layout/Email/text/default.ctp new file mode 100644 index 000000000..871dcfb48 --- /dev/null +++ b/cake-3.1/src/Template/Layout/Email/text/default.ctp @@ -0,0 +1,16 @@ + +fetch('content') ?> diff --git a/cake-3.1/src/Template/Layout/ajax.ctp b/cake-3.1/src/Template/Layout/ajax.ctp new file mode 100644 index 000000000..871dcfb48 --- /dev/null +++ b/cake-3.1/src/Template/Layout/ajax.ctp @@ -0,0 +1,16 @@ + +fetch('content') ?> diff --git a/cake-3.1/src/Template/Layout/default.ctp b/cake-3.1/src/Template/Layout/default.ctp new file mode 100644 index 000000000..c9a71e937 --- /dev/null +++ b/cake-3.1/src/Template/Layout/default.ctp @@ -0,0 +1,57 @@ + + + + + Html->charset() ?> + + + <?= $cakeDescription ?>: + <?= $this->fetch('title') ?> + + Html->meta('icon') ?> + + Html->css('base.css') ?> + Html->css('cake.css') ?> + + fetch('meta') ?> + fetch('css') ?> + fetch('script') ?> + + + + Flash->render() ?> +
+ fetch('content') ?> +
+
+
+ + diff --git a/cake-3.1/src/Template/Layout/error.ctp b/cake-3.1/src/Template/Layout/error.ctp new file mode 100644 index 000000000..e6945a8ad --- /dev/null +++ b/cake-3.1/src/Template/Layout/error.ctp @@ -0,0 +1,55 @@ + + + + + Html->charset() ?> + + <?= $cakeDescription ?>: + <?= $this->fetch('title') ?> + + Html->meta('icon') ?> + + Html->css('base.css') ?> + Html->css('cake.css') ?> + + fetch('meta') ?> + fetch('css') ?> + fetch('script') ?> + + +
+ +
+ Flash->render() ?> + + fetch('content') ?> +
+ +
+ + diff --git a/cake-3.1/src/Template/Layout/rss/default.ctp b/cake-3.1/src/Template/Layout/rss/default.ctp new file mode 100644 index 000000000..fdadcbf78 --- /dev/null +++ b/cake-3.1/src/Template/Layout/rss/default.ctp @@ -0,0 +1,14 @@ +fetch('title'); +endif; + +echo $this->Rss->document( + $this->Rss->channel( + array(), $channel, $this->fetch('content') + ) +); +?> diff --git a/cake-3.1/src/Template/Pages/home.ctp b/cake-3.1/src/Template/Pages/home.ctp new file mode 100644 index 000000000..99655bdbb --- /dev/null +++ b/cake-3.1/src/Template/Pages/home.ctp @@ -0,0 +1,192 @@ +layout = false; + +if (!Configure::read('debug')): + throw new NotFoundException(); +endif; + +$cakeDescription = 'CakePHP: the rapid development php framework'; +?> + + + + Html->charset() ?> + + + <?= $cakeDescription ?> + + Html->meta('icon') ?> + Html->css('base.css') ?> + Html->css('cake.css') ?> + + +
+
+ Html->image('http://cakephp.org/img/cake-logo.png') ?> +

Get the Ovens Ready

+
+
+
+
+ +
+

URL rewriting is not properly configured on your server.

+

+ 1) Help me configure it +

+

+ 2) I don't / can't use URL rewriting +

+
+
+ =')): ?> +

Your version of PHP is 5.4.16 or higher.

+ +

Your version of PHP is too low. You need PHP 5.4.16 or higher to use CakePHP.

+ + + +

Your version of PHP has the mbstring extension loaded.

+ +

Your version of PHP does NOT have the mbstring extension loaded.

; + + + +

Your version of PHP has the openssl extension loaded.

+ +

Your version of PHP has the mcrypt extension loaded.

+ +

Your version of PHP does NOT have the openssl or mcrypt extension loaded.

+ + + +

Your version of PHP has the intl extension loaded.

+ +

Your version of PHP does NOT have the intl extension loaded.

+ +
+
+ +

Your tmp directory is writable.

+ +

Your tmp directory is NOT writable.

+ + + +

Your logs directory is writable.

+ +

Your logs directory is NOT writable.

+ + + + +

The Engine is being used for core caching. To change the config edit config/app.php

+ +

Your cache is NOT working. Please check the settings in config/app.php

+ +
+
+
+
+ connect(); + } catch (Exception $connectionError) { + $connected = false; + $errorMsg = $connectionError->getMessage(); + if (method_exists($connectionError, 'getAttributes')): + $attributes = $connectionError->getAttributes(); + if (isset($errorMsg['message'])): + $errorMsg .= '
' . $attributes['message']; + endif; + endif; + } + ?> + +

CakePHP is able to connect to the database.

+ +

CakePHP is NOT able to connect to the database.

+ +
+
+
+
+

Editing this Page

+
    +
  • To change the content of this page, edit: src/Template/Pages/home.ctp.
  • +
  • You can also add some CSS styles for your pages at: webroot/css/.
  • +
+
+ +
+
+
+
+

More about Cake

+

+ CakePHP is a rapid development framework for PHP which uses commonly known design patterns like Front Controller and MVC. +

+

+ Our primary goal is to provide a structured framework that enables PHP users at all levels to rapidly develop robust web applications, without any loss to flexibility. +

+ +
+
+
+
+
+ + diff --git a/cake-3.1/src/View/AjaxView.php b/cake-3.1/src/View/AjaxView.php new file mode 100644 index 000000000..cd017d860 --- /dev/null +++ b/cake-3.1/src/View/AjaxView.php @@ -0,0 +1,49 @@ +response->type('ajax'); + } +} diff --git a/cake-3.1/src/View/AppView.php b/cake-3.1/src/View/AppView.php new file mode 100644 index 000000000..fd52ba695 --- /dev/null +++ b/cake-3.1/src/View/AppView.php @@ -0,0 +1,40 @@ +loadHelper('Html');` + * + * @return void + */ + public function initialize() + { + } +} diff --git a/fuel-1.8-dev/fuel/app/themes/.gitkeep b/cake-3.1/src/View/Helper/empty similarity index 100% rename from fuel-1.8-dev/fuel/app/themes/.gitkeep rename to cake-3.1/src/View/Helper/empty diff --git a/fuel-1.8-dev/fuel/app/tmp/.gitkeep b/cake-3.1/tests/Fixture/empty similarity index 100% rename from fuel-1.8-dev/fuel/app/tmp/.gitkeep rename to cake-3.1/tests/Fixture/empty diff --git a/fuel-1.8-dev/fuel/app/vendor/.gitkeep b/cake-3.1/tests/TestCase/Controller/Component/empty similarity index 100% rename from fuel-1.8-dev/fuel/app/vendor/.gitkeep rename to cake-3.1/tests/TestCase/Controller/Component/empty diff --git a/fuel-1.8-dev/fuel/app/views/.gitkeep b/cake-3.1/tests/TestCase/Model/Behavior/empty similarity index 100% rename from fuel-1.8-dev/fuel/app/views/.gitkeep rename to cake-3.1/tests/TestCase/Model/Behavior/empty diff --git a/fuel-1.8-dev/fuel/packages/.gitkeep b/cake-3.1/tests/TestCase/View/Helper/empty similarity index 100% rename from fuel-1.8-dev/fuel/packages/.gitkeep rename to cake-3.1/tests/TestCase/View/Helper/empty diff --git a/cake-3.1/tests/bootstrap.php b/cake-3.1/tests/bootstrap.php new file mode 100644 index 000000000..e56aa9df2 --- /dev/null +++ b/cake-3.1/tests/bootstrap.php @@ -0,0 +1,8 @@ +.column,.row.collapse>.columns{padding-left:0;padding-right:0}.row.collapse .row{margin-left:0;margin-right:0}.row .row{margin:0 -0.9375rem;max-width:none;width:auto}.row .row:before,.row .row:after{content:" ";display:table}.row .row:after{clear:both}.row .row.collapse{margin:0;max-width:none;width:auto}.row .row.collapse:before,.row .row.collapse:after{content:" ";display:table}.row .row.collapse:after{clear:both}.column,.columns{padding-left:0.9375rem;padding-right:0.9375rem;width:100%;float:left}.column+.column:last-child,.columns+.column:last-child,.column+.columns:last-child,.columns+.columns:last-child{float:right}.column+.column.end,.columns+.column.end,.column+.columns.end,.columns+.columns.end{float:left}@media only screen{.small-push-0{position:relative;left:0;right:auto}.small-pull-0{position:relative;right:0;left:auto}.small-push-1{position:relative;left:8.33333%;right:auto}.small-pull-1{position:relative;right:8.33333%;left:auto}.small-push-2{position:relative;left:16.66667%;right:auto}.small-pull-2{position:relative;right:16.66667%;left:auto}.small-push-3{position:relative;left:25%;right:auto}.small-pull-3{position:relative;right:25%;left:auto}.small-push-4{position:relative;left:33.33333%;right:auto}.small-pull-4{position:relative;right:33.33333%;left:auto}.small-push-5{position:relative;left:41.66667%;right:auto}.small-pull-5{position:relative;right:41.66667%;left:auto}.small-push-6{position:relative;left:50%;right:auto}.small-pull-6{position:relative;right:50%;left:auto}.small-push-7{position:relative;left:58.33333%;right:auto}.small-pull-7{position:relative;right:58.33333%;left:auto}.small-push-8{position:relative;left:66.66667%;right:auto}.small-pull-8{position:relative;right:66.66667%;left:auto}.small-push-9{position:relative;left:75%;right:auto}.small-pull-9{position:relative;right:75%;left:auto}.small-push-10{position:relative;left:83.33333%;right:auto}.small-pull-10{position:relative;right:83.33333%;left:auto}.small-push-11{position:relative;left:91.66667%;right:auto}.small-pull-11{position:relative;right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375rem;padding-right:0.9375rem;float:left}.small-1{width:8.33333%}.small-2{width:16.66667%}.small-3{width:25%}.small-4{width:33.33333%}.small-5{width:41.66667%}.small-6{width:50%}.small-7{width:58.33333%}.small-8{width:66.66667%}.small-9{width:75%}.small-10{width:83.33333%}.small-11{width:91.66667%}.small-12{width:100%}.small-offset-0{margin-left:0 !important}.small-offset-1{margin-left:8.33333% !important}.small-offset-2{margin-left:16.66667% !important}.small-offset-3{margin-left:25% !important}.small-offset-4{margin-left:33.33333% !important}.small-offset-5{margin-left:41.66667% !important}.small-offset-6{margin-left:50% !important}.small-offset-7{margin-left:58.33333% !important}.small-offset-8{margin-left:66.66667% !important}.small-offset-9{margin-left:75% !important}.small-offset-10{margin-left:83.33333% !important}.small-offset-11{margin-left:91.66667% !important}.small-reset-order{float:left;left:auto;margin-left:0;margin-right:0;right:auto}.column.small-centered,.columns.small-centered{margin-left:auto;margin-right:auto;float:none}.column.small-uncentered,.columns.small-uncentered{float:left;margin-left:0;margin-right:0}.column.small-centered:last-child,.columns.small-centered:last-child{float:none}.column.small-uncentered:last-child,.columns.small-uncentered:last-child{float:left}.column.small-uncentered.opposite,.columns.small-uncentered.opposite{float:right}.row.small-collapse>.column,.row.small-collapse>.columns{padding-left:0;padding-right:0}.row.small-collapse .row{margin-left:0;margin-right:0}.row.small-uncollapse>.column,.row.small-uncollapse>.columns{padding-left:0.9375rem;padding-right:0.9375rem;float:left}}@media only screen and (min-width: 40.0625em){.medium-push-0{position:relative;left:0;right:auto}.medium-pull-0{position:relative;right:0;left:auto}.medium-push-1{position:relative;left:8.33333%;right:auto}.medium-pull-1{position:relative;right:8.33333%;left:auto}.medium-push-2{position:relative;left:16.66667%;right:auto}.medium-pull-2{position:relative;right:16.66667%;left:auto}.medium-push-3{position:relative;left:25%;right:auto}.medium-pull-3{position:relative;right:25%;left:auto}.medium-push-4{position:relative;left:33.33333%;right:auto}.medium-pull-4{position:relative;right:33.33333%;left:auto}.medium-push-5{position:relative;left:41.66667%;right:auto}.medium-pull-5{position:relative;right:41.66667%;left:auto}.medium-push-6{position:relative;left:50%;right:auto}.medium-pull-6{position:relative;right:50%;left:auto}.medium-push-7{position:relative;left:58.33333%;right:auto}.medium-pull-7{position:relative;right:58.33333%;left:auto}.medium-push-8{position:relative;left:66.66667%;right:auto}.medium-pull-8{position:relative;right:66.66667%;left:auto}.medium-push-9{position:relative;left:75%;right:auto}.medium-pull-9{position:relative;right:75%;left:auto}.medium-push-10{position:relative;left:83.33333%;right:auto}.medium-pull-10{position:relative;right:83.33333%;left:auto}.medium-push-11{position:relative;left:91.66667%;right:auto}.medium-pull-11{position:relative;right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375rem;padding-right:0.9375rem;float:left}.medium-1{width:8.33333%}.medium-2{width:16.66667%}.medium-3{width:25%}.medium-4{width:33.33333%}.medium-5{width:41.66667%}.medium-6{width:50%}.medium-7{width:58.33333%}.medium-8{width:66.66667%}.medium-9{width:75%}.medium-10{width:83.33333%}.medium-11{width:91.66667%}.medium-12{width:100%}.medium-offset-0{margin-left:0 !important}.medium-offset-1{margin-left:8.33333% !important}.medium-offset-2{margin-left:16.66667% !important}.medium-offset-3{margin-left:25% !important}.medium-offset-4{margin-left:33.33333% !important}.medium-offset-5{margin-left:41.66667% !important}.medium-offset-6{margin-left:50% !important}.medium-offset-7{margin-left:58.33333% !important}.medium-offset-8{margin-left:66.66667% !important}.medium-offset-9{margin-left:75% !important}.medium-offset-10{margin-left:83.33333% !important}.medium-offset-11{margin-left:91.66667% !important}.medium-reset-order{float:left;left:auto;margin-left:0;margin-right:0;right:auto}.column.medium-centered,.columns.medium-centered{margin-left:auto;margin-right:auto;float:none}.column.medium-uncentered,.columns.medium-uncentered{float:left;margin-left:0;margin-right:0}.column.medium-centered:last-child,.columns.medium-centered:last-child{float:none}.column.medium-uncentered:last-child,.columns.medium-uncentered:last-child{float:left}.column.medium-uncentered.opposite,.columns.medium-uncentered.opposite{float:right}.row.medium-collapse>.column,.row.medium-collapse>.columns{padding-left:0;padding-right:0}.row.medium-collapse .row{margin-left:0;margin-right:0}.row.medium-uncollapse>.column,.row.medium-uncollapse>.columns{padding-left:0.9375rem;padding-right:0.9375rem;float:left}.push-0{position:relative;left:0;right:auto}.pull-0{position:relative;right:0;left:auto}.push-1{position:relative;left:8.33333%;right:auto}.pull-1{position:relative;right:8.33333%;left:auto}.push-2{position:relative;left:16.66667%;right:auto}.pull-2{position:relative;right:16.66667%;left:auto}.push-3{position:relative;left:25%;right:auto}.pull-3{position:relative;right:25%;left:auto}.push-4{position:relative;left:33.33333%;right:auto}.pull-4{position:relative;right:33.33333%;left:auto}.push-5{position:relative;left:41.66667%;right:auto}.pull-5{position:relative;right:41.66667%;left:auto}.push-6{position:relative;left:50%;right:auto}.pull-6{position:relative;right:50%;left:auto}.push-7{position:relative;left:58.33333%;right:auto}.pull-7{position:relative;right:58.33333%;left:auto}.push-8{position:relative;left:66.66667%;right:auto}.pull-8{position:relative;right:66.66667%;left:auto}.push-9{position:relative;left:75%;right:auto}.pull-9{position:relative;right:75%;left:auto}.push-10{position:relative;left:83.33333%;right:auto}.pull-10{position:relative;right:83.33333%;left:auto}.push-11{position:relative;left:91.66667%;right:auto}.pull-11{position:relative;right:91.66667%;left:auto}}@media only screen and (min-width: 64.0625em){.large-push-0{position:relative;left:0;right:auto}.large-pull-0{position:relative;right:0;left:auto}.large-push-1{position:relative;left:8.33333%;right:auto}.large-pull-1{position:relative;right:8.33333%;left:auto}.large-push-2{position:relative;left:16.66667%;right:auto}.large-pull-2{position:relative;right:16.66667%;left:auto}.large-push-3{position:relative;left:25%;right:auto}.large-pull-3{position:relative;right:25%;left:auto}.large-push-4{position:relative;left:33.33333%;right:auto}.large-pull-4{position:relative;right:33.33333%;left:auto}.large-push-5{position:relative;left:41.66667%;right:auto}.large-pull-5{position:relative;right:41.66667%;left:auto}.large-push-6{position:relative;left:50%;right:auto}.large-pull-6{position:relative;right:50%;left:auto}.large-push-7{position:relative;left:58.33333%;right:auto}.large-pull-7{position:relative;right:58.33333%;left:auto}.large-push-8{position:relative;left:66.66667%;right:auto}.large-pull-8{position:relative;right:66.66667%;left:auto}.large-push-9{position:relative;left:75%;right:auto}.large-pull-9{position:relative;right:75%;left:auto}.large-push-10{position:relative;left:83.33333%;right:auto}.large-pull-10{position:relative;right:83.33333%;left:auto}.large-push-11{position:relative;left:91.66667%;right:auto}.large-pull-11{position:relative;right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375rem;padding-right:0.9375rem;float:left}.large-1{width:8.33333%}.large-2{width:16.66667%}.large-3{width:25%}.large-4{width:33.33333%}.large-5{width:41.66667%}.large-6{width:50%}.large-7{width:58.33333%}.large-8{width:66.66667%}.large-9{width:75%}.large-10{width:83.33333%}.large-11{width:91.66667%}.large-12{width:100%}.large-offset-0{margin-left:0 !important}.large-offset-1{margin-left:8.33333% !important}.large-offset-2{margin-left:16.66667% !important}.large-offset-3{margin-left:25% !important}.large-offset-4{margin-left:33.33333% !important}.large-offset-5{margin-left:41.66667% !important}.large-offset-6{margin-left:50% !important}.large-offset-7{margin-left:58.33333% !important}.large-offset-8{margin-left:66.66667% !important}.large-offset-9{margin-left:75% !important}.large-offset-10{margin-left:83.33333% !important}.large-offset-11{margin-left:91.66667% !important}.large-reset-order{float:left;left:auto;margin-left:0;margin-right:0;right:auto}.column.large-centered,.columns.large-centered{margin-left:auto;margin-right:auto;float:none}.column.large-uncentered,.columns.large-uncentered{float:left;margin-left:0;margin-right:0}.column.large-centered:last-child,.columns.large-centered:last-child{float:none}.column.large-uncentered:last-child,.columns.large-uncentered:last-child{float:left}.column.large-uncentered.opposite,.columns.large-uncentered.opposite{float:right}.row.large-collapse>.column,.row.large-collapse>.columns{padding-left:0;padding-right:0}.row.large-collapse .row{margin-left:0;margin-right:0}.row.large-uncollapse>.column,.row.large-uncollapse>.columns{padding-left:0.9375rem;padding-right:0.9375rem;float:left}.push-0{position:relative;left:0;right:auto}.pull-0{position:relative;right:0;left:auto}.push-1{position:relative;left:8.33333%;right:auto}.pull-1{position:relative;right:8.33333%;left:auto}.push-2{position:relative;left:16.66667%;right:auto}.pull-2{position:relative;right:16.66667%;left:auto}.push-3{position:relative;left:25%;right:auto}.pull-3{position:relative;right:25%;left:auto}.push-4{position:relative;left:33.33333%;right:auto}.pull-4{position:relative;right:33.33333%;left:auto}.push-5{position:relative;left:41.66667%;right:auto}.pull-5{position:relative;right:41.66667%;left:auto}.push-6{position:relative;left:50%;right:auto}.pull-6{position:relative;right:50%;left:auto}.push-7{position:relative;left:58.33333%;right:auto}.pull-7{position:relative;right:58.33333%;left:auto}.push-8{position:relative;left:66.66667%;right:auto}.pull-8{position:relative;right:66.66667%;left:auto}.push-9{position:relative;left:75%;right:auto}.pull-9{position:relative;right:75%;left:auto}.push-10{position:relative;left:83.33333%;right:auto}.pull-10{position:relative;right:83.33333%;left:auto}.push-11{position:relative;left:91.66667%;right:auto}.pull-11{position:relative;right:91.66667%;left:auto}}button,.button{-webkit-appearance:none;-moz-appearance:none;border-radius:0;border-style:solid;border-width:0;cursor:pointer;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-weight:normal;line-height:normal;margin:0 0 1.25rem;position:relative;text-align:center;text-decoration:none;display:inline-block;padding:1rem 2rem 1.0625rem 2rem;font-size:1rem;background-color:#008CBA;border-color:#007095;color:#fff;transition:background-color 300ms ease-out}button:hover,button:focus,.button:hover,.button:focus{background-color:#007095}button:hover,button:focus,.button:hover,.button:focus{color:#fff}button.secondary,.button.secondary{background-color:#e7e7e7;border-color:#b9b9b9;color:#333}button.secondary:hover,button.secondary:focus,.button.secondary:hover,.button.secondary:focus{background-color:#b9b9b9}button.secondary:hover,button.secondary:focus,.button.secondary:hover,.button.secondary:focus{color:#333}button.success,.button.success{background-color:#43AC6A;border-color:#368a55;color:#fff}button.success:hover,button.success:focus,.button.success:hover,.button.success:focus{background-color:#368a55}button.success:hover,button.success:focus,.button.success:hover,.button.success:focus{color:#fff}button.alert,.button.alert{background-color:#f04124;border-color:#cf2a0e;color:#fff}button.alert:hover,button.alert:focus,.button.alert:hover,.button.alert:focus{background-color:#cf2a0e}button.alert:hover,button.alert:focus,.button.alert:hover,.button.alert:focus{color:#fff}button.warning,.button.warning{background-color:#f08a24;border-color:#cf6e0e;color:#fff}button.warning:hover,button.warning:focus,.button.warning:hover,.button.warning:focus{background-color:#cf6e0e}button.warning:hover,button.warning:focus,.button.warning:hover,.button.warning:focus{color:#fff}button.info,.button.info{background-color:#a0d3e8;border-color:#61b6d9;color:#333}button.info:hover,button.info:focus,.button.info:hover,.button.info:focus{background-color:#61b6d9}button.info:hover,button.info:focus,.button.info:hover,.button.info:focus{color:#fff}button.large,.button.large{padding:1.125rem 2.25rem 1.1875rem 2.25rem;font-size:1.25rem}button.small,.button.small{padding:0.875rem 1.75rem 0.9375rem 1.75rem;font-size:0.8125rem}button.tiny,.button.tiny{padding:0.625rem 1.25rem 0.6875rem 1.25rem;font-size:0.6875rem}button.expand,.button.expand{padding-left:0;padding-right:0;width:100%}button.left-align,.button.left-align{text-align:left;text-indent:0.75rem}button.right-align,.button.right-align{text-align:right;padding-right:0.75rem}button.radius,.button.radius{border-radius:3px}button.round,.button.round{border-radius:1000px}button.disabled,button[disabled],.button.disabled,.button[disabled]{background-color:#008CBA;border-color:#007095;color:#fff;box-shadow:none;cursor:default;opacity:0.7}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{background-color:#007095}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{color:#fff}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{background-color:#008CBA}button.disabled.secondary,button[disabled].secondary,.button.disabled.secondary,.button[disabled].secondary{background-color:#e7e7e7;border-color:#b9b9b9;color:#333;box-shadow:none;cursor:default;opacity:0.7}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{background-color:#b9b9b9}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{color:#333}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{background-color:#e7e7e7}button.disabled.success,button[disabled].success,.button.disabled.success,.button[disabled].success{background-color:#43AC6A;border-color:#368a55;color:#fff;box-shadow:none;cursor:default;opacity:0.7}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{background-color:#368a55}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{color:#fff}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{background-color:#43AC6A}button.disabled.alert,button[disabled].alert,.button.disabled.alert,.button[disabled].alert{background-color:#f04124;border-color:#cf2a0e;color:#fff;box-shadow:none;cursor:default;opacity:0.7}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{background-color:#cf2a0e}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{color:#fff}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{background-color:#f04124}button.disabled.warning,button[disabled].warning,.button.disabled.warning,.button[disabled].warning{background-color:#f08a24;border-color:#cf6e0e;color:#fff;box-shadow:none;cursor:default;opacity:0.7}button.disabled.warning:hover,button.disabled.warning:focus,button[disabled].warning:hover,button[disabled].warning:focus,.button.disabled.warning:hover,.button.disabled.warning:focus,.button[disabled].warning:hover,.button[disabled].warning:focus{background-color:#cf6e0e}button.disabled.warning:hover,button.disabled.warning:focus,button[disabled].warning:hover,button[disabled].warning:focus,.button.disabled.warning:hover,.button.disabled.warning:focus,.button[disabled].warning:hover,.button[disabled].warning:focus{color:#fff}button.disabled.warning:hover,button.disabled.warning:focus,button[disabled].warning:hover,button[disabled].warning:focus,.button.disabled.warning:hover,.button.disabled.warning:focus,.button[disabled].warning:hover,.button[disabled].warning:focus{background-color:#f08a24}button.disabled.info,button[disabled].info,.button.disabled.info,.button[disabled].info{background-color:#a0d3e8;border-color:#61b6d9;color:#333;box-shadow:none;cursor:default;opacity:0.7}button.disabled.info:hover,button.disabled.info:focus,button[disabled].info:hover,button[disabled].info:focus,.button.disabled.info:hover,.button.disabled.info:focus,.button[disabled].info:hover,.button[disabled].info:focus{background-color:#61b6d9}button.disabled.info:hover,button.disabled.info:focus,button[disabled].info:hover,button[disabled].info:focus,.button.disabled.info:hover,.button.disabled.info:focus,.button[disabled].info:hover,.button[disabled].info:focus{color:#fff}button.disabled.info:hover,button.disabled.info:focus,button[disabled].info:hover,button[disabled].info:focus,.button.disabled.info:hover,.button.disabled.info:focus,.button[disabled].info:hover,.button[disabled].info:focus{background-color:#a0d3e8}button::-moz-focus-inner{border:0;padding:0}@media only screen and (min-width: 40.0625em){button,.button{display:inline-block}}form{margin:0 0 1rem}form .row .row{margin:0 -0.5rem}form .row .row .column,form .row .row .columns{padding:0 0.5rem}form .row .row.collapse{margin:0}form .row .row.collapse .column,form .row .row.collapse .columns{padding:0}form .row .row.collapse input{-webkit-border-bottom-right-radius:0;-webkit-border-top-right-radius:0;border-bottom-right-radius:0;border-top-right-radius:0}form .row input.column,form .row input.columns,form .row textarea.column,form .row textarea.columns{padding-left:0.5rem}label{color:#4d4d4d;cursor:pointer;display:block;font-size:0.875rem;font-weight:normal;line-height:1.5;margin-bottom:0}label.right{float:none !important;text-align:right}label.inline{margin:0 0 1rem 0;padding:0.5625rem 0}label small{text-transform:capitalize;color:#676767}.prefix,.postfix{border-style:solid;border-width:1px;display:block;font-size:0.875rem;height:2.3125rem;line-height:2.3125rem;overflow:visible;padding-bottom:0;padding-top:0;position:relative;text-align:center;width:100%;z-index:2}.postfix.button{border-color:true}.prefix.button{border:none;padding-left:0;padding-right:0;padding-bottom:0;padding-top:0;text-align:center}.prefix.button.radius{border-radius:0;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.postfix.button.radius{border-radius:0;-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.prefix.button.round{border-radius:0;-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}.postfix.button.round{border-radius:0;-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}span.prefix,label.prefix{background:#f2f2f2;border-right:none;color:#333;border-color:#ccc}span.postfix,label.postfix{background:#f2f2f2;color:#333;border-color:#ccc}input[type="text"],input[type="password"],input[type="date"],input[type="datetime"],input[type="datetime-local"],input[type="month"],input[type="week"],input[type="email"],input[type="number"],input[type="search"],input[type="tel"],input[type="time"],input[type="url"],input[type="color"],textarea{-webkit-appearance:none;-moz-appearance:none;border-radius:0;background-color:#fff;border-style:solid;border-width:1px;border-color:#ccc;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);color:rgba(0,0,0,0.75);display:block;font-family:inherit;font-size:0.875rem;height:2.3125rem;margin:0 0 1rem 0;padding:0.5rem;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:border-color 0.15s linear,background 0.15s linear;-moz-transition:border-color 0.15s linear,background 0.15s linear;-ms-transition:border-color 0.15s linear,background 0.15s linear;-o-transition:border-color 0.15s linear,background 0.15s linear;transition:border-color 0.15s linear,background 0.15s linear}input[type="text"]:focus,input[type="password"]:focus,input[type="date"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="month"]:focus,input[type="week"]:focus,input[type="email"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="time"]:focus,input[type="url"]:focus,input[type="color"]:focus,textarea:focus{background:#fafafa;border-color:#999;outline:none}input[type="text"]:disabled,input[type="password"]:disabled,input[type="date"]:disabled,input[type="datetime"]:disabled,input[type="datetime-local"]:disabled,input[type="month"]:disabled,input[type="week"]:disabled,input[type="email"]:disabled,input[type="number"]:disabled,input[type="search"]:disabled,input[type="tel"]:disabled,input[type="time"]:disabled,input[type="url"]:disabled,input[type="color"]:disabled,textarea:disabled{background-color:#ddd;cursor:default}input[type="text"][disabled],input[type="text"][readonly],fieldset[disabled] input[type="text"],input[type="password"][disabled],input[type="password"][readonly],fieldset[disabled] input[type="password"],input[type="date"][disabled],input[type="date"][readonly],fieldset[disabled] input[type="date"],input[type="datetime"][disabled],input[type="datetime"][readonly],fieldset[disabled] input[type="datetime"],input[type="datetime-local"][disabled],input[type="datetime-local"][readonly],fieldset[disabled] input[type="datetime-local"],input[type="month"][disabled],input[type="month"][readonly],fieldset[disabled] input[type="month"],input[type="week"][disabled],input[type="week"][readonly],fieldset[disabled] input[type="week"],input[type="email"][disabled],input[type="email"][readonly],fieldset[disabled] input[type="email"],input[type="number"][disabled],input[type="number"][readonly],fieldset[disabled] input[type="number"],input[type="search"][disabled],input[type="search"][readonly],fieldset[disabled] input[type="search"],input[type="tel"][disabled],input[type="tel"][readonly],fieldset[disabled] input[type="tel"],input[type="time"][disabled],input[type="time"][readonly],fieldset[disabled] input[type="time"],input[type="url"][disabled],input[type="url"][readonly],fieldset[disabled] input[type="url"],input[type="color"][disabled],input[type="color"][readonly],fieldset[disabled] input[type="color"],textarea[disabled],textarea[readonly],fieldset[disabled] textarea{background-color:#ddd;cursor:default}input[type="text"].radius,input[type="password"].radius,input[type="date"].radius,input[type="datetime"].radius,input[type="datetime-local"].radius,input[type="month"].radius,input[type="week"].radius,input[type="email"].radius,input[type="number"].radius,input[type="search"].radius,input[type="tel"].radius,input[type="time"].radius,input[type="url"].radius,input[type="color"].radius,textarea.radius{border-radius:3px}form .row .prefix-radius.row.collapse input,form .row .prefix-radius.row.collapse textarea,form .row .prefix-radius.row.collapse select,form .row .prefix-radius.row.collapse button{border-radius:0;-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}form .row .prefix-radius.row.collapse .prefix{border-radius:0;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}form .row .postfix-radius.row.collapse input,form .row .postfix-radius.row.collapse textarea,form .row .postfix-radius.row.collapse select,form .row .postfix-radius.row.collapse button{border-radius:0;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}form .row .postfix-radius.row.collapse .postfix{border-radius:0;-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}form .row .prefix-round.row.collapse input,form .row .prefix-round.row.collapse textarea,form .row .prefix-round.row.collapse select,form .row .prefix-round.row.collapse button{border-radius:0;-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}form .row .prefix-round.row.collapse .prefix{border-radius:0;-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}form .row .postfix-round.row.collapse input,form .row .postfix-round.row.collapse textarea,form .row .postfix-round.row.collapse select,form .row .postfix-round.row.collapse button{border-radius:0;-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}form .row .postfix-round.row.collapse .postfix{border-radius:0;-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}input[type="submit"]{-webkit-appearance:none;-moz-appearance:none;border-radius:0}textarea[rows]{height:auto}textarea{max-width:100%}::-webkit-input-placeholder{color:#ccc}:-moz-placeholder{color:#ccc}::-moz-placeholder{color:#ccc}:-ms-input-placeholder{color:#ccc}select{-webkit-appearance:none !important;-moz-appearance:none !important;background-color:#FAFAFA;border-radius:0;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMTJweCIgeT0iMHB4IiB3aWR0aD0iMjRweCIgaGVpZ2h0PSIzcHgiIHZpZXdCb3g9IjAgMCA2IDMiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDYgMyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBvbHlnb24gcG9pbnRzPSI1Ljk5MiwwIDIuOTkyLDMgLTAuMDA4LDAgIi8+PC9zdmc+);background-position:100% center;background-repeat:no-repeat;border-style:solid;border-width:1px;border-color:#ccc;color:rgba(0,0,0,0.75);font-family:inherit;font-size:0.875rem;line-height:normal;padding:0.5rem;border-radius:0;height:2.3125rem}select::-ms-expand{display:none}select.radius{border-radius:3px}select:hover{background-color:#f3f3f3;border-color:#999}select:disabled{background-color:#ddd;cursor:default}select[multiple]{height:auto}input[type="file"],input[type="checkbox"],input[type="radio"],select{margin:0 0 1rem 0}input[type="checkbox"]+label,input[type="radio"]+label{display:inline-block;margin-left:0.5rem;margin-right:1rem;margin-bottom:0;vertical-align:baseline}input[type="file"]{width:100%}fieldset{border:1px solid #ddd;margin:1.125rem 0;padding:1.25rem}fieldset legend{background:#fff;font-weight:bold;margin-left:-0.1875rem;margin:0;padding:0 0.1875rem}[data-abide] .error small.error,[data-abide] .error span.error,[data-abide] span.error,[data-abide] small.error{display:block;font-size:0.75rem;font-style:italic;font-weight:normal;margin-bottom:1rem;margin-top:-1px;padding:0.375rem 0.5625rem 0.5625rem;background:#f04124;color:#fff}[data-abide] span.error,[data-abide] small.error{display:none}span.error,small.error{display:block;font-size:0.75rem;font-style:italic;font-weight:normal;margin-bottom:1rem;margin-top:-1px;padding:0.375rem 0.5625rem 0.5625rem;background:#f04124;color:#fff}.error input,.error textarea,.error select{margin-bottom:0}.error input[type="checkbox"],.error input[type="radio"]{margin-bottom:1rem}.error label,.error label.error{color:#f04124}.error small.error{display:block;font-size:0.75rem;font-style:italic;font-weight:normal;margin-bottom:1rem;margin-top:-1px;padding:0.375rem 0.5625rem 0.5625rem;background:#f04124;color:#fff}.error>label>small{background:transparent;color:#676767;display:inline;font-size:60%;font-style:normal;margin:0;padding:0;text-transform:capitalize}.error span.error-message{display:block}input.error,textarea.error,select.error{margin-bottom:0}label.error{color:#f04124}meta.foundation-mq-topbar{font-family:"/only screen and (min-width:40.0625em)/";width:40.0625em}.contain-to-grid{width:100%;background:#333}.contain-to-grid .top-bar{margin-bottom:0}.fixed{position:fixed;top:0;width:100%;z-index:99;left:0}.fixed.expanded:not(.top-bar){height:auto;max-height:100%;overflow-y:auto;width:100%}.fixed.expanded:not(.top-bar) .title-area{position:fixed;width:100%;z-index:99}.fixed.expanded:not(.top-bar) .top-bar-section{margin-top:2.8125rem;z-index:98}.top-bar{background:#333;height:2.8125rem;line-height:2.8125rem;margin-bottom:0;overflow:hidden;position:relative}.top-bar ul{list-style:none;margin-bottom:0}.top-bar .row{max-width:none}.top-bar form,.top-bar input,.top-bar select{margin-bottom:0}.top-bar input,.top-bar select{font-size:0.75rem;height:1.75rem;padding-bottom:.35rem;padding-top:.35rem}.top-bar .button,.top-bar button{font-size:0.75rem;margin-bottom:0;padding-bottom:0.4125rem;padding-top:0.4125rem}@media only screen and (max-width: 40em){.top-bar .button,.top-bar button{position:relative;top:-1px}}.top-bar .title-area{margin:0;position:relative}.top-bar .name{font-size:16px;height:2.8125rem;margin:0}.top-bar .name h1,.top-bar .name h2,.top-bar .name h3,.top-bar .name h4,.top-bar .name p,.top-bar .name span{font-size:1.0625rem;line-height:2.8125rem;margin:0}.top-bar .name h1 a,.top-bar .name h2 a,.top-bar .name h3 a,.top-bar .name h4 a,.top-bar .name p a,.top-bar .name span a{color:#fff;display:block;font-weight:normal;padding:0 0.9375rem;width:75%}.top-bar .toggle-topbar{position:absolute;right:0;top:0}.top-bar .toggle-topbar a{color:#fff;display:block;font-size:0.8125rem;font-weight:bold;height:2.8125rem;line-height:2.8125rem;padding:0 0.9375rem;position:relative;text-transform:uppercase}.top-bar .toggle-topbar.menu-icon{margin-top:-16px;top:50%}.top-bar .toggle-topbar.menu-icon a{color:#fff;height:34px;line-height:33px;padding:0 2.5rem 0 0.9375rem;position:relative}.top-bar .toggle-topbar.menu-icon a span::after{content:"";display:block;height:0;position:absolute;margin-top:-8px;top:50%;right:0.9375rem;box-shadow:0 0 0 1px #fff,0 7px 0 1px #fff,0 14px 0 1px #fff;width:16px}.top-bar .toggle-topbar.menu-icon a span:hover:after{box-shadow:0 0 0 1px "",0 7px 0 1px "",0 14px 0 1px ""}.top-bar.expanded{background:transparent;height:auto}.top-bar.expanded .title-area{background:#333}.top-bar.expanded .toggle-topbar a{color:#888}.top-bar.expanded .toggle-topbar a span::after{box-shadow:0 0 0 1px #888,0 7px 0 1px #888,0 14px 0 1px #888}@media screen and (-webkit-min-device-pixel-ratio: 0){.top-bar.expanded .top-bar-section .has-dropdown.moved>.dropdown,.top-bar.expanded .top-bar-section .dropdown{clip:initial}.top-bar.expanded .top-bar-section .has-dropdown:not(.moved)>ul{padding:0}}.top-bar-section{left:0;position:relative;width:auto;transition:left 300ms ease-out}.top-bar-section ul{display:block;font-size:16px;height:auto;margin:0;padding:0;width:100%}.top-bar-section .divider,.top-bar-section [role="separator"]{border-top:solid 1px #1a1a1a;clear:both;height:1px;width:100%}.top-bar-section ul li{background:#333}.top-bar-section ul li>a{color:#fff;display:block;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:0.8125rem;font-weight:normal;padding-left:0.9375rem;padding:12px 0 12px 0.9375rem;text-transform:none;width:100%}.top-bar-section ul li>a.button{font-size:0.8125rem;padding-left:0.9375rem;padding-right:0.9375rem;background-color:#008CBA;border-color:#007095;color:#fff}.top-bar-section ul li>a.button:hover,.top-bar-section ul li>a.button:focus{background-color:#007095}.top-bar-section ul li>a.button:hover,.top-bar-section ul li>a.button:focus{color:#fff}.top-bar-section ul li>a.button.secondary{background-color:#e7e7e7;border-color:#b9b9b9;color:#333}.top-bar-section ul li>a.button.secondary:hover,.top-bar-section ul li>a.button.secondary:focus{background-color:#b9b9b9}.top-bar-section ul li>a.button.secondary:hover,.top-bar-section ul li>a.button.secondary:focus{color:#333}.top-bar-section ul li>a.button.success{background-color:#43AC6A;border-color:#368a55;color:#fff}.top-bar-section ul li>a.button.success:hover,.top-bar-section ul li>a.button.success:focus{background-color:#368a55}.top-bar-section ul li>a.button.success:hover,.top-bar-section ul li>a.button.success:focus{color:#fff}.top-bar-section ul li>a.button.alert{background-color:#f04124;border-color:#cf2a0e;color:#fff}.top-bar-section ul li>a.button.alert:hover,.top-bar-section ul li>a.button.alert:focus{background-color:#cf2a0e}.top-bar-section ul li>a.button.alert:hover,.top-bar-section ul li>a.button.alert:focus{color:#fff}.top-bar-section ul li>a.button.warning{background-color:#f08a24;border-color:#cf6e0e;color:#fff}.top-bar-section ul li>a.button.warning:hover,.top-bar-section ul li>a.button.warning:focus{background-color:#cf6e0e}.top-bar-section ul li>a.button.warning:hover,.top-bar-section ul li>a.button.warning:focus{color:#fff}.top-bar-section ul li>a.button.info{background-color:#a0d3e8;border-color:#61b6d9;color:#333}.top-bar-section ul li>a.button.info:hover,.top-bar-section ul li>a.button.info:focus{background-color:#61b6d9}.top-bar-section ul li>a.button.info:hover,.top-bar-section ul li>a.button.info:focus{color:#fff}.top-bar-section ul li>button{font-size:0.8125rem;padding-left:0.9375rem;padding-right:0.9375rem;background-color:#008CBA;border-color:#007095;color:#fff}.top-bar-section ul li>button:hover,.top-bar-section ul li>button:focus{background-color:#007095}.top-bar-section ul li>button:hover,.top-bar-section ul li>button:focus{color:#fff}.top-bar-section ul li>button.secondary{background-color:#e7e7e7;border-color:#b9b9b9;color:#333}.top-bar-section ul li>button.secondary:hover,.top-bar-section ul li>button.secondary:focus{background-color:#b9b9b9}.top-bar-section ul li>button.secondary:hover,.top-bar-section ul li>button.secondary:focus{color:#333}.top-bar-section ul li>button.success{background-color:#43AC6A;border-color:#368a55;color:#fff}.top-bar-section ul li>button.success:hover,.top-bar-section ul li>button.success:focus{background-color:#368a55}.top-bar-section ul li>button.success:hover,.top-bar-section ul li>button.success:focus{color:#fff}.top-bar-section ul li>button.alert{background-color:#f04124;border-color:#cf2a0e;color:#fff}.top-bar-section ul li>button.alert:hover,.top-bar-section ul li>button.alert:focus{background-color:#cf2a0e}.top-bar-section ul li>button.alert:hover,.top-bar-section ul li>button.alert:focus{color:#fff}.top-bar-section ul li>button.warning{background-color:#f08a24;border-color:#cf6e0e;color:#fff}.top-bar-section ul li>button.warning:hover,.top-bar-section ul li>button.warning:focus{background-color:#cf6e0e}.top-bar-section ul li>button.warning:hover,.top-bar-section ul li>button.warning:focus{color:#fff}.top-bar-section ul li>button.info{background-color:#a0d3e8;border-color:#61b6d9;color:#333}.top-bar-section ul li>button.info:hover,.top-bar-section ul li>button.info:focus{background-color:#61b6d9}.top-bar-section ul li>button.info:hover,.top-bar-section ul li>button.info:focus{color:#fff}.top-bar-section ul li:hover:not(.has-form)>a{background-color:#555;color:#fff;background:#222}.top-bar-section ul li.active>a{background:#008CBA;color:#fff}.top-bar-section ul li.active>a:hover{background:#0078a0;color:#fff}.top-bar-section .has-form{padding:0.9375rem}.top-bar-section .has-dropdown{position:relative}.top-bar-section .has-dropdown>a:after{border:inset 5px;content:"";display:block;height:0;width:0;border-color:transparent transparent transparent rgba(255,255,255,0.4);border-left-style:solid;margin-right:0.9375rem;margin-top:-4.5px;position:absolute;top:50%;right:0}.top-bar-section .has-dropdown.moved{position:static}.top-bar-section .has-dropdown.moved>.dropdown{position:static !important;height:auto;width:auto;overflow:visible;clip:auto;display:block;position:absolute !important;width:100%}.top-bar-section .has-dropdown.moved>a:after{display:none}.top-bar-section .dropdown{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px;display:block;padding:0;position:absolute;top:0;z-index:99;left:100%}.top-bar-section .dropdown li{height:auto;width:100%}.top-bar-section .dropdown li a{font-weight:normal;padding:8px 0.9375rem}.top-bar-section .dropdown li a.parent-link{font-weight:normal}.top-bar-section .dropdown li.title h5,.top-bar-section .dropdown li.parent-link{margin-bottom:0;margin-top:0;font-size:1.125rem}.top-bar-section .dropdown li.title h5 a,.top-bar-section .dropdown li.parent-link a{color:#fff;display:block}.top-bar-section .dropdown li.title h5 a:hover,.top-bar-section .dropdown li.parent-link a:hover{background:none}.top-bar-section .dropdown li.has-form{padding:8px 0.9375rem}.top-bar-section .dropdown li .button,.top-bar-section .dropdown li button{top:auto}.top-bar-section .dropdown label{color:#777;font-size:0.625rem;font-weight:bold;margin-bottom:0;padding:8px 0.9375rem 2px;text-transform:uppercase}.js-generated{display:block}@media only screen and (min-width: 40.0625em){.top-bar{background:#333;overflow:visible}.top-bar:before,.top-bar:after{content:" ";display:table}.top-bar:after{clear:both}.top-bar .toggle-topbar{display:none}.top-bar .title-area{float:left}.top-bar .name h1 a,.top-bar .name h2 a,.top-bar .name h3 a,.top-bar .name h4 a,.top-bar .name h5 a,.top-bar .name h6 a{width:auto}.top-bar input,.top-bar select,.top-bar .button,.top-bar button{font-size:0.875rem;height:1.75rem;position:relative;top:0.53125rem}.top-bar.expanded{background:#333}.contain-to-grid .top-bar{margin-bottom:0;margin:0 auto;max-width:62.5rem}.top-bar-section{transition:none 0 0;left:0 !important}.top-bar-section ul{display:inline;height:auto !important;width:auto}.top-bar-section ul li{float:left}.top-bar-section ul li .js-generated{display:none}.top-bar-section li.hover>a:not(.button){background-color:#555;background:#222;color:#fff}.top-bar-section li:not(.has-form) a:not(.button){background:#333;line-height:2.8125rem;padding:0 0.9375rem}.top-bar-section li:not(.has-form) a:not(.button):hover{background-color:#555;background:#222}.top-bar-section li.active:not(.has-form) a:not(.button){background:#008CBA;color:#fff;line-height:2.8125rem;padding:0 0.9375rem}.top-bar-section li.active:not(.has-form) a:not(.button):hover{background:#0078a0;color:#fff}.top-bar-section .has-dropdown>a{padding-right:2.1875rem !important}.top-bar-section .has-dropdown>a:after{border:inset 5px;content:"";display:block;height:0;width:0;border-color:rgba(255,255,255,0.4) transparent transparent transparent;border-top-style:solid;margin-top:-2.5px;top:1.40625rem}.top-bar-section .has-dropdown.moved{position:relative}.top-bar-section .has-dropdown.moved>.dropdown{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px;display:block}.top-bar-section .has-dropdown.hover>.dropdown,.top-bar-section .has-dropdown.not-click:hover>.dropdown{position:static !important;height:auto;width:auto;overflow:visible;clip:auto;display:block;position:absolute !important}.top-bar-section .has-dropdown>a:focus+.dropdown{position:static !important;height:auto;width:auto;overflow:visible;clip:auto;display:block;position:absolute !important}.top-bar-section .has-dropdown .dropdown li.has-dropdown>a:after{border:none;content:"\00bb";top:0.1875rem;right:5px}.top-bar-section .dropdown{left:0;background:transparent;min-width:100%;top:auto}.top-bar-section .dropdown li a{background:#333;color:#fff;line-height:2.8125rem;padding:12px 0.9375rem;white-space:nowrap}.top-bar-section .dropdown li:not(.has-form):not(.active)>a:not(.button){background:#333;color:#fff}.top-bar-section .dropdown li:not(.has-form):not(.active):hover>a:not(.button){background-color:#555;color:#fff;background:#222}.top-bar-section .dropdown li label{background:#333;white-space:nowrap}.top-bar-section .dropdown li .dropdown{left:100%;top:0}.top-bar-section>ul>.divider,.top-bar-section>ul>[role="separator"]{border-right:solid 1px #4e4e4e;border-bottom:none;border-top:none;clear:none;height:2.8125rem;width:0}.top-bar-section .has-form{background:#333;height:2.8125rem;padding:0 0.9375rem}.top-bar-section .right li .dropdown{left:auto;right:0}.top-bar-section .right li .dropdown li .dropdown{right:100%}.top-bar-section .left li .dropdown{right:auto;left:0}.top-bar-section .left li .dropdown li .dropdown{left:100%}.no-js .top-bar-section ul li:hover>a{background-color:#555;background:#222;color:#fff}.no-js .top-bar-section ul li:active>a{background:#008CBA;color:#fff}.no-js .top-bar-section .has-dropdown:hover>.dropdown{position:static !important;height:auto;width:auto;overflow:visible;clip:auto;display:block;position:absolute !important}.no-js .top-bar-section .has-dropdown>a:focus+.dropdown{position:static !important;height:auto;width:auto;overflow:visible;clip:auto;display:block;position:absolute !important}}.breadcrumbs{border-style:solid;border-width:1px;display:block;list-style:none;margin-left:0;overflow:hidden;padding:0.5625rem 0.875rem 0.5625rem;background-color:#f4f4f4;border-color:#dcdcdc;border-radius:3px}.breadcrumbs>*{color:#008CBA;float:left;font-size:0.6875rem;line-height:0.6875rem;margin:0;text-transform:uppercase}.breadcrumbs>*:hover a,.breadcrumbs>*:focus a{text-decoration:underline}.breadcrumbs>* a{color:#008CBA}.breadcrumbs>*.current{color:#333;cursor:default}.breadcrumbs>*.current a{color:#333;cursor:default}.breadcrumbs>*.current:hover,.breadcrumbs>*.current:hover a,.breadcrumbs>*.current:focus,.breadcrumbs>*.current:focus a{text-decoration:none}.breadcrumbs>*.unavailable{color:#999}.breadcrumbs>*.unavailable a{color:#999}.breadcrumbs>*.unavailable:hover,.breadcrumbs>*.unavailable:hover a,.breadcrumbs>*.unavailable:focus,.breadcrumbs>*.unavailable a:focus{color:#999;cursor:not-allowed;text-decoration:none}.breadcrumbs>*:before{color:#aaa;content:"/";margin:0 0.75rem;position:relative;top:1px}.breadcrumbs>*:first-child:before{content:" ";margin:0}[aria-label="breadcrumbs"] [aria-hidden="true"]:after{content:"/"}.alert-box{border-style:solid;border-width:1px;display:block;font-size:0.8125rem;font-weight:normal;margin-bottom:1.25rem;padding:0.875rem 1.5rem 0.875rem 0.875rem;position:relative;transition:opacity 300ms ease-out;background-color:#008CBA;border-color:#0078a0;color:#fff}.alert-box .close{right:0.25rem;background:inherit;color:#333;font-size:1.375rem;line-height:.9;margin-top:-0.6875rem;opacity:0.3;padding:0 6px 4px;position:absolute;top:50%}.alert-box .close:hover,.alert-box .close:focus{opacity:0.5}.alert-box.radius{border-radius:3px}.alert-box.round{border-radius:1000px}.alert-box.success{background-color:#43AC6A;border-color:#3a945b;color:#fff}.alert-box.alert{background-color:#f04124;border-color:#de2d0f;color:#fff}.alert-box.secondary{background-color:#e7e7e7;border-color:#c7c7c7;color:#4f4f4f}.alert-box.warning{background-color:#f08a24;border-color:#de770f;color:#fff}.alert-box.info{background-color:#a0d3e8;border-color:#74bfdd;color:#4f4f4f}.alert-box.alert-close{opacity:0}.inline-list{list-style:none;margin-left:-1.375rem;margin-right:0;margin:0 auto 1.0625rem auto;overflow:hidden;padding:0}.inline-list>li{display:block;float:left;list-style:none;margin-left:1.375rem}.inline-list>li>*{display:block}.button-group{list-style:none;margin:0;left:0}.button-group:before,.button-group:after{content:" ";display:table}.button-group:after{clear:both}.button-group.even-2 li{display:inline-block;margin:0 -2px;width:50%}.button-group.even-2 li>button,.button-group.even-2 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-2 li:first-child button,.button-group.even-2 li:first-child .button{border-left:0}.button-group.even-2 li button,.button-group.even-2 li .button{width:100%}.button-group.even-3 li{display:inline-block;margin:0 -2px;width:33.33333%}.button-group.even-3 li>button,.button-group.even-3 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-3 li:first-child button,.button-group.even-3 li:first-child .button{border-left:0}.button-group.even-3 li button,.button-group.even-3 li .button{width:100%}.button-group.even-4 li{display:inline-block;margin:0 -2px;width:25%}.button-group.even-4 li>button,.button-group.even-4 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-4 li:first-child button,.button-group.even-4 li:first-child .button{border-left:0}.button-group.even-4 li button,.button-group.even-4 li .button{width:100%}.button-group.even-5 li{display:inline-block;margin:0 -2px;width:20%}.button-group.even-5 li>button,.button-group.even-5 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-5 li:first-child button,.button-group.even-5 li:first-child .button{border-left:0}.button-group.even-5 li button,.button-group.even-5 li .button{width:100%}.button-group.even-6 li{display:inline-block;margin:0 -2px;width:16.66667%}.button-group.even-6 li>button,.button-group.even-6 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-6 li:first-child button,.button-group.even-6 li:first-child .button{border-left:0}.button-group.even-6 li button,.button-group.even-6 li .button{width:100%}.button-group.even-7 li{display:inline-block;margin:0 -2px;width:14.28571%}.button-group.even-7 li>button,.button-group.even-7 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-7 li:first-child button,.button-group.even-7 li:first-child .button{border-left:0}.button-group.even-7 li button,.button-group.even-7 li .button{width:100%}.button-group.even-8 li{display:inline-block;margin:0 -2px;width:12.5%}.button-group.even-8 li>button,.button-group.even-8 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-8 li:first-child button,.button-group.even-8 li:first-child .button{border-left:0}.button-group.even-8 li button,.button-group.even-8 li .button{width:100%}.button-group>li{display:inline-block;margin:0 -2px}.button-group>li>button,.button-group>li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group>li:first-child button,.button-group>li:first-child .button{border-left:0}.button-group.stack>li{display:block;margin:0;float:none}.button-group.stack>li>button,.button-group.stack>li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.stack>li:first-child button,.button-group.stack>li:first-child .button{border-left:0}.button-group.stack>li>button,.button-group.stack>li .button{border-color:rgba(255,255,255,0.5);border-left-width:0;border-top:1px solid;display:block;margin:0}.button-group.stack>li>button{width:100%}.button-group.stack>li:first-child button,.button-group.stack>li:first-child .button{border-top:0}.button-group.stack-for-small>li{display:inline-block;margin:0 -2px}.button-group.stack-for-small>li>button,.button-group.stack-for-small>li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.stack-for-small>li:first-child button,.button-group.stack-for-small>li:first-child .button{border-left:0}@media only screen and (max-width: 40em){.button-group.stack-for-small>li{display:block;margin:0}.button-group.stack-for-small>li>button,.button-group.stack-for-small>li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.stack-for-small>li:first-child button,.button-group.stack-for-small>li:first-child .button{border-left:0}.button-group.stack-for-small>li>button,.button-group.stack-for-small>li .button{border-color:rgba(255,255,255,0.5);border-left-width:0;border-top:1px solid;display:block;margin:0}.button-group.stack-for-small>li>button{width:100%}.button-group.stack-for-small>li:first-child button,.button-group.stack-for-small>li:first-child .button{border-top:0}}.button-group.radius>*{display:inline-block;margin:0 -2px}.button-group.radius>*>button,.button-group.radius>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.radius>*:first-child button,.button-group.radius>*:first-child .button{border-left:0}.button-group.radius>*,.button-group.radius>*>a,.button-group.radius>*>button,.button-group.radius>*>.button{border-radius:0}.button-group.radius>*:first-child,.button-group.radius>*:first-child>a,.button-group.radius>*:first-child>button,.button-group.radius>*:first-child>.button{-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.button-group.radius>*:last-child,.button-group.radius>*:last-child>a,.button-group.radius>*:last-child>button,.button-group.radius>*:last-child>.button{-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.button-group.radius.stack>*{display:block;margin:0}.button-group.radius.stack>*>button,.button-group.radius.stack>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.radius.stack>*:first-child button,.button-group.radius.stack>*:first-child .button{border-left:0}.button-group.radius.stack>*>button,.button-group.radius.stack>* .button{border-color:rgba(255,255,255,0.5);border-left-width:0;border-top:1px solid;display:block;margin:0}.button-group.radius.stack>*>button{width:100%}.button-group.radius.stack>*:first-child button,.button-group.radius.stack>*:first-child .button{border-top:0}.button-group.radius.stack>*,.button-group.radius.stack>*>a,.button-group.radius.stack>*>button,.button-group.radius.stack>*>.button{border-radius:0}.button-group.radius.stack>*:first-child,.button-group.radius.stack>*:first-child>a,.button-group.radius.stack>*:first-child>button,.button-group.radius.stack>*:first-child>.button{-webkit-top-left-radius:3px;-webkit-top-right-radius:3px;border-top-left-radius:3px;border-top-right-radius:3px}.button-group.radius.stack>*:last-child,.button-group.radius.stack>*:last-child>a,.button-group.radius.stack>*:last-child>button,.button-group.radius.stack>*:last-child>.button{-webkit-bottom-left-radius:3px;-webkit-bottom-right-radius:3px;border-bottom-left-radius:3px;border-bottom-right-radius:3px}@media only screen and (min-width: 40.0625em){.button-group.radius.stack-for-small>*{display:inline-block;margin:0 -2px}.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.radius.stack-for-small>*:first-child button,.button-group.radius.stack-for-small>*:first-child .button{border-left:0}.button-group.radius.stack-for-small>*,.button-group.radius.stack-for-small>*>a,.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>*>.button{border-radius:0}.button-group.radius.stack-for-small>*:first-child,.button-group.radius.stack-for-small>*:first-child>a,.button-group.radius.stack-for-small>*:first-child>button,.button-group.radius.stack-for-small>*:first-child>.button{-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.button-group.radius.stack-for-small>*:last-child,.button-group.radius.stack-for-small>*:last-child>a,.button-group.radius.stack-for-small>*:last-child>button,.button-group.radius.stack-for-small>*:last-child>.button{-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}}@media only screen and (max-width: 40em){.button-group.radius.stack-for-small>*{display:block;margin:0}.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.radius.stack-for-small>*:first-child button,.button-group.radius.stack-for-small>*:first-child .button{border-left:0}.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>* .button{border-color:rgba(255,255,255,0.5);border-left-width:0;border-top:1px solid;display:block;margin:0}.button-group.radius.stack-for-small>*>button{width:100%}.button-group.radius.stack-for-small>*:first-child button,.button-group.radius.stack-for-small>*:first-child .button{border-top:0}.button-group.radius.stack-for-small>*,.button-group.radius.stack-for-small>*>a,.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>*>.button{border-radius:0}.button-group.radius.stack-for-small>*:first-child,.button-group.radius.stack-for-small>*:first-child>a,.button-group.radius.stack-for-small>*:first-child>button,.button-group.radius.stack-for-small>*:first-child>.button{-webkit-top-left-radius:3px;-webkit-top-right-radius:3px;border-top-left-radius:3px;border-top-right-radius:3px}.button-group.radius.stack-for-small>*:last-child,.button-group.radius.stack-for-small>*:last-child>a,.button-group.radius.stack-for-small>*:last-child>button,.button-group.radius.stack-for-small>*:last-child>.button{-webkit-bottom-left-radius:3px;-webkit-bottom-right-radius:3px;border-bottom-left-radius:3px;border-bottom-right-radius:3px}}.button-group.round>*{display:inline-block;margin:0 -2px}.button-group.round>*>button,.button-group.round>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.round>*:first-child button,.button-group.round>*:first-child .button{border-left:0}.button-group.round>*,.button-group.round>*>a,.button-group.round>*>button,.button-group.round>*>.button{border-radius:0}.button-group.round>*:first-child,.button-group.round>*:first-child>a,.button-group.round>*:first-child>button,.button-group.round>*:first-child>.button{-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}.button-group.round>*:last-child,.button-group.round>*:last-child>a,.button-group.round>*:last-child>button,.button-group.round>*:last-child>.button{-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}.button-group.round.stack>*{display:block;margin:0}.button-group.round.stack>*>button,.button-group.round.stack>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.round.stack>*:first-child button,.button-group.round.stack>*:first-child .button{border-left:0}.button-group.round.stack>*>button,.button-group.round.stack>* .button{border-color:rgba(255,255,255,0.5);border-left-width:0;border-top:1px solid;display:block;margin:0}.button-group.round.stack>*>button{width:100%}.button-group.round.stack>*:first-child button,.button-group.round.stack>*:first-child .button{border-top:0}.button-group.round.stack>*,.button-group.round.stack>*>a,.button-group.round.stack>*>button,.button-group.round.stack>*>.button{border-radius:0}.button-group.round.stack>*:first-child,.button-group.round.stack>*:first-child>a,.button-group.round.stack>*:first-child>button,.button-group.round.stack>*:first-child>.button{-webkit-top-left-radius:1rem;-webkit-top-right-radius:1rem;border-top-left-radius:1rem;border-top-right-radius:1rem}.button-group.round.stack>*:last-child,.button-group.round.stack>*:last-child>a,.button-group.round.stack>*:last-child>button,.button-group.round.stack>*:last-child>.button{-webkit-bottom-left-radius:1rem;-webkit-bottom-right-radius:1rem;border-bottom-left-radius:1rem;border-bottom-right-radius:1rem}@media only screen and (min-width: 40.0625em){.button-group.round.stack-for-small>*{display:inline-block;margin:0 -2px}.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.round.stack-for-small>*:first-child button,.button-group.round.stack-for-small>*:first-child .button{border-left:0}.button-group.round.stack-for-small>*,.button-group.round.stack-for-small>*>a,.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>*>.button{border-radius:0}.button-group.round.stack-for-small>*:first-child,.button-group.round.stack-for-small>*:first-child>a,.button-group.round.stack-for-small>*:first-child>button,.button-group.round.stack-for-small>*:first-child>.button{-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}.button-group.round.stack-for-small>*:last-child,.button-group.round.stack-for-small>*:last-child>a,.button-group.round.stack-for-small>*:last-child>button,.button-group.round.stack-for-small>*:last-child>.button{-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}}@media only screen and (max-width: 40em){.button-group.round.stack-for-small>*{display:block;margin:0}.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.round.stack-for-small>*:first-child button,.button-group.round.stack-for-small>*:first-child .button{border-left:0}.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>* .button{border-color:rgba(255,255,255,0.5);border-left-width:0;border-top:1px solid;display:block;margin:0}.button-group.round.stack-for-small>*>button{width:100%}.button-group.round.stack-for-small>*:first-child button,.button-group.round.stack-for-small>*:first-child .button{border-top:0}.button-group.round.stack-for-small>*,.button-group.round.stack-for-small>*>a,.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>*>.button{border-radius:0}.button-group.round.stack-for-small>*:first-child,.button-group.round.stack-for-small>*:first-child>a,.button-group.round.stack-for-small>*:first-child>button,.button-group.round.stack-for-small>*:first-child>.button{-webkit-top-left-radius:1rem;-webkit-top-right-radius:1rem;border-top-left-radius:1rem;border-top-right-radius:1rem}.button-group.round.stack-for-small>*:last-child,.button-group.round.stack-for-small>*:last-child>a,.button-group.round.stack-for-small>*:last-child>button,.button-group.round.stack-for-small>*:last-child>.button{-webkit-bottom-left-radius:1rem;-webkit-bottom-right-radius:1rem;border-bottom-left-radius:1rem;border-bottom-right-radius:1rem}}.button-bar:before,.button-bar:after{content:" ";display:table}.button-bar:after{clear:both}.button-bar .button-group{float:left;margin-right:0.625rem}.button-bar .button-group div{overflow:hidden}.panel{border-style:solid;border-width:1px;border-color:#d8d8d8;margin-bottom:1.25rem;padding:1.25rem;background:#f2f2f2;color:#333}.panel>:first-child{margin-top:0}.panel>:last-child{margin-bottom:0}.panel h1,.panel h2,.panel h3,.panel h4,.panel h5,.panel h6,.panel p,.panel li,.panel dl{color:#333}.panel h1,.panel h2,.panel h3,.panel h4,.panel h5,.panel h6{line-height:1;margin-bottom:0.625rem}.panel h1.subheader,.panel h2.subheader,.panel h3.subheader,.panel h4.subheader,.panel h5.subheader,.panel h6.subheader{line-height:1.4}.panel.callout{border-style:solid;border-width:1px;border-color:#d8d8d8;margin-bottom:1.25rem;padding:1.25rem;background:#ecfaff;color:#333}.panel.callout>:first-child{margin-top:0}.panel.callout>:last-child{margin-bottom:0}.panel.callout h1,.panel.callout h2,.panel.callout h3,.panel.callout h4,.panel.callout h5,.panel.callout h6,.panel.callout p,.panel.callout li,.panel.callout dl{color:#333}.panel.callout h1,.panel.callout h2,.panel.callout h3,.panel.callout h4,.panel.callout h5,.panel.callout h6{line-height:1;margin-bottom:0.625rem}.panel.callout h1.subheader,.panel.callout h2.subheader,.panel.callout h3.subheader,.panel.callout h4.subheader,.panel.callout h5.subheader,.panel.callout h6.subheader{line-height:1.4}.panel.callout a:not(.button){color:#008CBA}.panel.callout a:not(.button):hover,.panel.callout a:not(.button):focus{color:#0078a0}.panel.radius{border-radius:3px}.dropdown.button,button.dropdown{position:relative;padding-right:3.5625rem}.dropdown.button::after,button.dropdown::after{border-color:#fff transparent transparent transparent;border-style:solid;content:"";display:block;height:0;position:absolute;top:50%;width:0}.dropdown.button::after,button.dropdown::after{border-width:0.375rem;right:1.40625rem;margin-top:-0.15625rem}.dropdown.button::after,button.dropdown::after{border-color:#fff transparent transparent transparent}.dropdown.button.tiny,button.dropdown.tiny{padding-right:2.625rem}.dropdown.button.tiny:after,button.dropdown.tiny:after{border-width:0.375rem;right:1.125rem;margin-top:-0.125rem}.dropdown.button.tiny::after,button.dropdown.tiny::after{border-color:#fff transparent transparent transparent}.dropdown.button.small,button.dropdown.small{padding-right:3.0625rem}.dropdown.button.small::after,button.dropdown.small::after{border-width:0.4375rem;right:1.3125rem;margin-top:-0.15625rem}.dropdown.button.small::after,button.dropdown.small::after{border-color:#fff transparent transparent transparent}.dropdown.button.large,button.dropdown.large{padding-right:3.625rem}.dropdown.button.large::after,button.dropdown.large::after{border-width:0.3125rem;right:1.71875rem;margin-top:-0.15625rem}.dropdown.button.large::after,button.dropdown.large::after{border-color:#fff transparent transparent transparent}.dropdown.button.secondary:after,button.dropdown.secondary:after{border-color:#333 transparent transparent transparent}.th{border:solid 4px #fff;box-shadow:0 0 0 1px rgba(0,0,0,0.2);display:inline-block;line-height:0;max-width:100%;transition:all 200ms ease-out}.th:hover,.th:focus{box-shadow:0 0 6px 1px rgba(0,140,186,0.5)}.th.radius{border-radius:3px}.pricing-table{border:solid 1px #ddd;margin-left:0;margin-bottom:1.25rem}.pricing-table *{list-style:none;line-height:1}.pricing-table .title{background-color:#333;color:#eee;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:1rem;font-weight:normal;padding:0.9375rem 1.25rem;text-align:center}.pricing-table .price{background-color:#F6F6F6;color:#333;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:2rem;font-weight:normal;padding:0.9375rem 1.25rem;text-align:center}.pricing-table .description{background-color:#fff;border-bottom:dotted 1px #ddd;color:#777;font-size:0.75rem;font-weight:normal;line-height:1.4;padding:0.9375rem;text-align:center}.pricing-table .bullet-item{background-color:#fff;border-bottom:dotted 1px #ddd;color:#333;font-size:0.875rem;font-weight:normal;padding:0.9375rem;text-align:center}.pricing-table .cta-button{background-color:#fff;padding:1.25rem 1.25rem 0;text-align:center}@-webkit-keyframes rotate{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotate{from{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}}.slideshow-wrapper{position:relative}.slideshow-wrapper ul{list-style-type:none;margin:0}.slideshow-wrapper ul li,.slideshow-wrapper ul li .orbit-caption{display:none}.slideshow-wrapper ul li:first-child{display:block}.slideshow-wrapper .orbit-container{background-color:transparent}.slideshow-wrapper .orbit-container li{display:block}.slideshow-wrapper .orbit-container li .orbit-caption{display:block}.slideshow-wrapper .orbit-container .orbit-bullets li{display:inline-block}.slideshow-wrapper .preloader{border-radius:1000px;animation-duration:1.5s;animation-iteration-count:infinite;animation-name:rotate;animation-timing-function:linear;border-color:#555 #fff;border:solid 3px;display:block;height:40px;left:50%;margin-left:-20px;margin-top:-20px;position:absolute;top:50%;width:40px}.orbit-container{background:none;overflow:hidden;position:relative;width:100%}.orbit-container .orbit-slides-container{list-style:none;margin:0;padding:0;position:relative;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);-o-transform:translateZ(0);transform:translateZ(0)}.orbit-container .orbit-slides-container img{display:block;max-width:100%}.orbit-container .orbit-slides-container>*{position:absolute;top:0;width:100%;margin-left:100%}.orbit-container .orbit-slides-container>*:first-child{margin-left:0}.orbit-container .orbit-slides-container>* .orbit-caption{bottom:0;position:absolute;background-color:rgba(51,51,51,0.8);color:#fff;font-size:0.875rem;padding:0.625rem 0.875rem;width:100%}.orbit-container .orbit-slide-number{left:10px;background:transparent;color:#fff;font-size:12px;position:absolute;top:10px;z-index:10}.orbit-container .orbit-slide-number span{font-weight:700;padding:0.3125rem}.orbit-container .orbit-timer{position:absolute;top:12px;right:10px;height:6px;width:100px;z-index:10}.orbit-container .orbit-timer .orbit-progress{height:3px;background-color:rgba(255,255,255,0.3);display:block;width:0;position:relative;right:20px;top:5px}.orbit-container .orbit-timer>span{border:solid 4px #fff;border-bottom:none;border-top:none;display:none;height:14px;position:absolute;top:0;width:11px;right:0}.orbit-container .orbit-timer.paused>span{top:0;width:11px;height:14px;border:inset 8px;border-left-style:solid;border-color:transparent;border-left-color:#fff;right:-4px}.orbit-container .orbit-timer.paused>span.dark{border-left-color:#333}.orbit-container:hover .orbit-timer>span{display:block}.orbit-container .orbit-prev,.orbit-container .orbit-next{background-color:transparent;color:white;height:60px;line-height:50px;margin-top:-25px;position:absolute;text-indent:-9999px !important;top:45%;width:36px;z-index:10}.orbit-container .orbit-prev:hover,.orbit-container .orbit-next:hover{background-color:rgba(0,0,0,0.3)}.orbit-container .orbit-prev>span,.orbit-container .orbit-next>span{border:inset 10px;display:block;height:0;margin-top:-10px;position:absolute;top:50%;width:0}.orbit-container .orbit-prev{left:0}.orbit-container .orbit-prev>span{border-right-style:solid;border-color:transparent;border-right-color:#fff}.orbit-container .orbit-prev:hover>span{border-right-color:#fff}.orbit-container .orbit-next{right:0}.orbit-container .orbit-next>span{border-color:transparent;border-left-style:solid;border-left-color:#fff;left:50%;margin-left:-4px}.orbit-container .orbit-next:hover>span{border-left-color:#fff}.orbit-bullets-container{text-align:center}.orbit-bullets{display:block;float:none;margin:0 auto 30px auto;overflow:hidden;position:relative;text-align:center;top:10px}.orbit-bullets li{background:#ccc;cursor:pointer;display:inline-block;float:none;height:0.5625rem;margin-right:6px;width:0.5625rem;border-radius:1000px}.orbit-bullets li.active{background:#999}.orbit-bullets li:last-child{margin-right:0}.touch .orbit-container .orbit-prev,.touch .orbit-container .orbit-next{display:none}.touch .orbit-bullets{display:none}@media only screen and (min-width: 40.0625em){.touch .orbit-container .orbit-prev,.touch .orbit-container .orbit-next{display:inherit}.touch .orbit-bullets{display:block}}@media only screen and (max-width: 40em){.orbit-stack-on-small .orbit-slides-container{height:auto !important}.orbit-stack-on-small .orbit-slides-container>*{margin:0 !important;opacity:1 !important;position:relative}.orbit-stack-on-small .orbit-slide-number{display:none}.orbit-timer{display:none}.orbit-next,.orbit-prev{display:none}.orbit-bullets{display:none}}[data-magellan-expedition],[data-magellan-expedition-clone]{background:#fff;min-width:100%;padding:10px;z-index:50}[data-magellan-expedition] .sub-nav,[data-magellan-expedition-clone] .sub-nav{margin-bottom:0}[data-magellan-expedition] .sub-nav dd,[data-magellan-expedition-clone] .sub-nav dd{margin-bottom:0}[data-magellan-expedition] .sub-nav a,[data-magellan-expedition-clone] .sub-nav a{line-height:1.8em}.icon-bar{display:inline-block;font-size:0;width:100%;background:#333}.icon-bar>*{display:block;float:left;font-size:1rem;margin:0 auto;padding:1.25rem;text-align:center;width:25%}.icon-bar>* i,.icon-bar>* img{display:block;margin:0 auto}.icon-bar>* i+label,.icon-bar>* img+label{margin-top:.0625rem}.icon-bar>* i{font-size:1.875rem;vertical-align:middle}.icon-bar>* img{height:1.875rem;width:1.875rem}.icon-bar.label-right>* i,.icon-bar.label-right>* img{display:inline-block;margin:0 .0625rem 0 0}.icon-bar.label-right>* i+label,.icon-bar.label-right>* img+label{margin-top:0}.icon-bar.label-right>* label{display:inline-block}.icon-bar.vertical.label-right>*{text-align:left}.icon-bar.vertical,.icon-bar.small-vertical{height:100%;width:auto}.icon-bar.vertical .item,.icon-bar.small-vertical .item{float:none;margin:auto;width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.medium-vertical{height:100%;width:auto}.icon-bar.medium-vertical .item{float:none;margin:auto;width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.large-vertical{height:100%;width:auto}.icon-bar.large-vertical .item{float:none;margin:auto;width:auto}}.icon-bar>*{font-size:1rem;padding:1.25rem}.icon-bar>* i+label,.icon-bar>* img+label{margin-top:.0625rem;font-size:1rem}.icon-bar>* i{font-size:1.875rem}.icon-bar>* img{height:1.875rem;width:1.875rem}.icon-bar>* label{color:#fff}.icon-bar>* i{color:#fff}.icon-bar>a:hover{background:#008CBA}.icon-bar>a:hover label{color:#fff}.icon-bar>a:hover i{color:#fff}.icon-bar>a.active{background:#008CBA}.icon-bar>a.active label{color:#fff}.icon-bar>a.active i{color:#fff}.icon-bar .item.disabled{cursor:not-allowed;opacity:0.7;pointer-events:none}.icon-bar .item.disabled>*{opacity:0.7;cursor:not-allowed}.icon-bar.two-up .item{width:50%}.icon-bar.two-up.vertical .item,.icon-bar.two-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.two-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.two-up.large-vertical .item{width:auto}}.icon-bar.three-up .item{width:33.3333%}.icon-bar.three-up.vertical .item,.icon-bar.three-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.three-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.three-up.large-vertical .item{width:auto}}.icon-bar.four-up .item{width:25%}.icon-bar.four-up.vertical .item,.icon-bar.four-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.four-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.four-up.large-vertical .item{width:auto}}.icon-bar.five-up .item{width:20%}.icon-bar.five-up.vertical .item,.icon-bar.five-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.five-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.five-up.large-vertical .item{width:auto}}.icon-bar.six-up .item{width:16.66667%}.icon-bar.six-up.vertical .item,.icon-bar.six-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.six-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.six-up.large-vertical .item{width:auto}}.icon-bar.seven-up .item{width:14.28571%}.icon-bar.seven-up.vertical .item,.icon-bar.seven-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.seven-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.seven-up.large-vertical .item{width:auto}}.icon-bar.eight-up .item{width:12.5%}.icon-bar.eight-up.vertical .item,.icon-bar.eight-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.eight-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.eight-up.large-vertical .item{width:auto}}.icon-bar.two-up .item{width:50%}.icon-bar.two-up.vertical .item,.icon-bar.two-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.two-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.two-up.large-vertical .item{width:auto}}.icon-bar.three-up .item{width:33.3333%}.icon-bar.three-up.vertical .item,.icon-bar.three-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.three-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.three-up.large-vertical .item{width:auto}}.icon-bar.four-up .item{width:25%}.icon-bar.four-up.vertical .item,.icon-bar.four-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.four-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.four-up.large-vertical .item{width:auto}}.icon-bar.five-up .item{width:20%}.icon-bar.five-up.vertical .item,.icon-bar.five-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.five-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.five-up.large-vertical .item{width:auto}}.icon-bar.six-up .item{width:16.66667%}.icon-bar.six-up.vertical .item,.icon-bar.six-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.six-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.six-up.large-vertical .item{width:auto}}.icon-bar.seven-up .item{width:14.28571%}.icon-bar.seven-up.vertical .item,.icon-bar.seven-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.seven-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.seven-up.large-vertical .item{width:auto}}.icon-bar.eight-up .item{width:12.5%}.icon-bar.eight-up.vertical .item,.icon-bar.eight-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.eight-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.eight-up.large-vertical .item{width:auto}}.tabs{margin-bottom:0 !important;margin-left:0}.tabs:before,.tabs:after{content:" ";display:table}.tabs:after{clear:both}.tabs dd,.tabs .tab-title{float:left;list-style:none;margin-bottom:0 !important;position:relative}.tabs dd>a,.tabs .tab-title>a{display:block;background-color:#EFEFEF;color:#222;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:1rem;padding:1rem 2rem}.tabs dd>a:hover,.tabs .tab-title>a:hover{background-color:#e1e1e1}.tabs dd.active a,.tabs .tab-title.active a{background-color:#fff;color:#222}.tabs.radius dd:first-child a,.tabs.radius .tab:first-child a{-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.tabs.radius dd:last-child a,.tabs.radius .tab:last-child a{-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.tabs.vertical dd,.tabs.vertical .tab-title{position:inherit;float:none;display:block;top:auto}.tabs-content{margin-bottom:1.5rem;width:100%}.tabs-content:before,.tabs-content:after{content:" ";display:table}.tabs-content:after{clear:both}.tabs-content>.content{display:none;float:left;padding:0.9375rem 0;width:100%}.tabs-content>.content.active{display:block;float:none}.tabs-content>.content.contained{padding:0.9375rem}.tabs-content.vertical{display:block}.tabs-content.vertical>.content{padding:0 0.9375rem}@media only screen and (min-width: 40.0625em){.tabs.vertical{float:left;margin:0;margin-bottom:1.25rem !important;max-width:20%;width:20%}.tabs-content.vertical{float:left;margin-left:-1px;max-width:80%;padding-left:1rem;width:80%}}.no-js .tabs-content>.content{display:block;float:none}ul.pagination{display:block;margin-left:-0.3125rem;min-height:1.5rem}ul.pagination li{color:#222;font-size:0.875rem;height:1.5rem;margin-left:0.3125rem}ul.pagination li a,ul.pagination li button{border-radius:3px;transition:background-color 300ms ease-out;background:none;color:#999;display:block;font-size:1em;font-weight:normal;line-height:inherit;padding:0.0625rem 0.625rem 0.0625rem}ul.pagination li:hover a,ul.pagination li a:focus,ul.pagination li:hover button,ul.pagination li button:focus{background:#e6e6e6}ul.pagination li.unavailable a,ul.pagination li.unavailable button{cursor:default;color:#999}ul.pagination li.unavailable:hover a,ul.pagination li.unavailable a:focus,ul.pagination li.unavailable:hover button,ul.pagination li.unavailable button:focus{background:transparent}ul.pagination li.current a,ul.pagination li.current button{background:#008CBA;color:#fff;cursor:default;font-weight:bold}ul.pagination li.current a:hover,ul.pagination li.current a:focus,ul.pagination li.current button:hover,ul.pagination li.current button:focus{background:#008CBA}ul.pagination li{display:block;float:left}.pagination-centered{text-align:center}.pagination-centered ul.pagination li{display:inline-block;float:none}.side-nav{display:block;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;list-style-position:outside;list-style-type:none;margin:0;padding:0.875rem 0}.side-nav li{font-size:0.875rem;font-weight:normal;margin:0 0 0.4375rem 0}.side-nav li a:not(.button){color:#008CBA;display:block;margin:0;padding:0.4375rem 0.875rem}.side-nav li a:not(.button):hover,.side-nav li a:not(.button):focus{background:rgba(0,0,0,0.025);color:#1cc7ff}.side-nav li a:not(.button):active{color:#1cc7ff}.side-nav li.active>a:first-child:not(.button){color:#1cc7ff;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-weight:normal}.side-nav li.divider{border-top:1px solid;height:0;list-style:none;padding:0;border-top-color:#e6e6e6}.side-nav li.heading{color:#008CBA;font-size:0.875rem;font-weight:bold;text-transform:uppercase}.accordion{margin-bottom:0}.accordion:before,.accordion:after{content:" ";display:table}.accordion:after{clear:both}.accordion .accordion-navigation,.accordion dd{display:block;margin-bottom:0 !important}.accordion .accordion-navigation.active>a,.accordion dd.active>a{background:#e8e8e8}.accordion .accordion-navigation>a,.accordion dd>a{background:#EFEFEF;color:#222;display:block;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:1rem;padding:1rem}.accordion .accordion-navigation>a:hover,.accordion dd>a:hover{background:#e3e3e3}.accordion .accordion-navigation>.content,.accordion dd>.content{display:none;padding:0.9375rem}.accordion .accordion-navigation>.content.active,.accordion dd>.content.active{background:#fff;display:block}.text-left{text-align:left !important}.text-right{text-align:right !important}.text-center{text-align:center !important}.text-justify{text-align:justify !important}@media only screen and (max-width: 40em){.small-only-text-left{text-align:left !important}.small-only-text-right{text-align:right !important}.small-only-text-center{text-align:center !important}.small-only-text-justify{text-align:justify !important}}@media only screen{.small-text-left{text-align:left !important}.small-text-right{text-align:right !important}.small-text-center{text-align:center !important}.small-text-justify{text-align:justify !important}}@media only screen and (min-width: 40.0625em) and (max-width: 64em){.medium-only-text-left{text-align:left !important}.medium-only-text-right{text-align:right !important}.medium-only-text-center{text-align:center !important}.medium-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 40.0625em){.medium-text-left{text-align:left !important}.medium-text-right{text-align:right !important}.medium-text-center{text-align:center !important}.medium-text-justify{text-align:justify !important}}@media only screen and (min-width: 64.0625em) and (max-width: 90em){.large-only-text-left{text-align:left !important}.large-only-text-right{text-align:right !important}.large-only-text-center{text-align:center !important}.large-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 64.0625em){.large-text-left{text-align:left !important}.large-text-right{text-align:right !important}.large-text-center{text-align:center !important}.large-text-justify{text-align:justify !important}}@media only screen and (min-width: 90.0625em) and (max-width: 120em){.xlarge-only-text-left{text-align:left !important}.xlarge-only-text-right{text-align:right !important}.xlarge-only-text-center{text-align:center !important}.xlarge-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 90.0625em){.xlarge-text-left{text-align:left !important}.xlarge-text-right{text-align:right !important}.xlarge-text-center{text-align:center !important}.xlarge-text-justify{text-align:justify !important}}@media only screen and (min-width: 120.0625em) and (max-width: 6249999.9375em){.xxlarge-only-text-left{text-align:left !important}.xxlarge-only-text-right{text-align:right !important}.xxlarge-only-text-center{text-align:center !important}.xxlarge-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 120.0625em){.xxlarge-text-left{text-align:left !important}.xxlarge-text-right{text-align:right !important}.xxlarge-text-center{text-align:center !important}.xxlarge-text-justify{text-align:justify !important}}div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}a{color:#008CBA;line-height:inherit;text-decoration:none}a:hover,a:focus{color:#0078a0}a img{border:none}p{font-family:inherit;font-size:1rem;font-weight:normal;line-height:1.6;margin-bottom:1.25rem;text-rendering:optimizeLegibility}p.lead{font-size:1.21875rem;line-height:1.6}p aside{font-size:0.875rem;font-style:italic;line-height:1.35}h1,h2,h3,h4,h5,h6{color:#222;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-style:normal;font-weight:normal;line-height:1.4;margin-bottom:0.5rem;margin-top:0.2rem;text-rendering:optimizeLegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{color:#6f6f6f;font-size:60%;line-height:0}h1{font-size:2.125rem}h2{font-size:1.6875rem}h3{font-size:1.375rem}h4{font-size:1.125rem}h5{font-size:1.125rem}h6{font-size:1rem}.subheader{line-height:1.4;color:#6f6f6f;font-weight:normal;margin-top:0.2rem;margin-bottom:0.5rem}hr{border:solid #ddd;border-width:1px 0 0;clear:both;height:0;margin:1.25rem 0 1.1875rem}em,i{font-style:italic;line-height:inherit}strong,b{font-weight:bold;line-height:inherit}small{font-size:60%;line-height:inherit}code{background-color:#f8f8f8;border-color:#dfdfdf;border-style:solid;border-width:1px;color:#333;font-family:Consolas,"Liberation Mono",Courier,monospace;font-weight:normal;padding:0.125rem 0.3125rem 0.0625rem}ul,ol,dl{font-family:inherit;font-size:1rem;line-height:1.6;list-style-position:outside;margin-bottom:1.25rem}ul{margin-left:1.1rem}ul.no-bullet{margin-left:0}ul.no-bullet li ul,ul.no-bullet li ol{margin-left:1.25rem;margin-bottom:0;list-style:none}ul li ul,ul li ol{margin-left:1.25rem;margin-bottom:0}ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}ul.square{list-style-type:square;margin-left:1.1rem}ul.circle{list-style-type:circle;margin-left:1.1rem}ul.disc{list-style-type:disc;margin-left:1.1rem}ul.no-bullet{list-style:none}ol{margin-left:1.4rem}ol li ul,ol li ol{margin-left:1.25rem;margin-bottom:0}dl dt{margin-bottom:0.3rem;font-weight:bold}dl dd{margin-bottom:0.75rem}abbr,acronym{text-transform:uppercase;font-size:90%;color:#222;cursor:help}abbr{text-transform:none}abbr[title]{border-bottom:1px dotted #ddd}blockquote{margin:0 0 1.25rem;padding:0.5625rem 1.25rem 0 1.1875rem;border-left:1px solid #ddd}blockquote cite{display:block;font-size:0.8125rem;color:#555}blockquote cite:before{content:"\2014 \0020"}blockquote cite a,blockquote cite a:visited{color:#555}blockquote,blockquote p{line-height:1.6;color:#6f6f6f}.vcard{display:inline-block;margin:0 0 1.25rem 0;border:1px solid #ddd;padding:0.625rem 0.75rem}.vcard li{margin:0;display:block}.vcard .fn{font-weight:bold;font-size:0.9375rem}.vevent .summary{font-weight:bold}.vevent abbr{cursor:default;text-decoration:none;font-weight:bold;border:none;padding:0 0.0625rem}@media only screen and (min-width: 40.0625em){h1,h2,h3,h4,h5,h6{line-height:1.4}h1{font-size:2.75rem}h2{font-size:2.3125rem}h3{font-size:1.6875rem}h4{font-size:1.4375rem}h5{font-size:1.125rem}h6{font-size:1rem}}.split.button{position:relative;padding-right:5.0625rem}.split.button span{display:block;height:100%;position:absolute;right:0;top:0;border-left:solid 1px}.split.button span:after{position:absolute;content:"";width:0;height:0;display:block;border-style:inset;top:50%;left:50%}.split.button span:active{background-color:rgba(0,0,0,0.1)}.split.button span{border-left-color:rgba(255,255,255,0.5)}.split.button span{width:3.09375rem}.split.button span:after{border-top-style:solid;border-width:0.375rem;margin-left:-0.375rem;top:48%}.split.button span:after{border-color:#fff transparent transparent transparent}.split.button.secondary span{border-left-color:rgba(255,255,255,0.5)}.split.button.secondary span:after{border-color:#fff transparent transparent transparent}.split.button.alert span{border-left-color:rgba(255,255,255,0.5)}.split.button.success span{border-left-color:rgba(255,255,255,0.5)}.split.button.tiny{padding-right:3.75rem}.split.button.tiny span{width:2.25rem}.split.button.tiny span:after{border-top-style:solid;border-width:0.375rem;margin-left:-0.375rem;top:48%}.split.button.small{padding-right:4.375rem}.split.button.small span{width:2.625rem}.split.button.small span:after{border-top-style:solid;border-width:0.4375rem;margin-left:-0.375rem;top:48%}.split.button.large{padding-right:5.5rem}.split.button.large span{width:3.4375rem}.split.button.large span:after{border-top-style:solid;border-width:0.3125rem;margin-left:-0.375rem;top:48%}.split.button.expand{padding-left:2rem}.split.button.secondary span:after{border-color:#333 transparent transparent transparent}.split.button.radius span{-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.split.button.round span{-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}.split.button.no-pip span:before{border-style:none}.split.button.no-pip span:after{border-style:none}.split.button.no-pip span>i{display:block;left:50%;margin-left:-0.28889em;margin-top:-0.48889em;position:absolute;top:50%}.reveal-modal-bg{background:#000;background:rgba(0,0,0,0.45);bottom:0;display:none;left:0;position:fixed;right:0;top:0;z-index:1004;left:0}.reveal-modal{border-radius:3px;display:none;position:absolute;top:0;visibility:hidden;width:100%;z-index:1005;left:0;background-color:#fff;padding:1.875rem;border:solid 1px #666;box-shadow:0 0 10px rgba(0,0,0,0.4)}@media only screen and (max-width: 40em){.reveal-modal{min-height:100vh}}.reveal-modal .column,.reveal-modal .columns{min-width:0}.reveal-modal>:first-child{margin-top:0}.reveal-modal>:last-child{margin-bottom:0}@media only screen and (min-width: 40.0625em){.reveal-modal{left:0;margin:0 auto;max-width:62.5rem;right:0;width:80%}}@media only screen and (min-width: 40.0625em){.reveal-modal{top:6.25rem}}.reveal-modal.radius{border-radius:3px}.reveal-modal.round{border-radius:1000px}.reveal-modal.collapse{padding:0}@media only screen and (min-width: 40.0625em){.reveal-modal.tiny{left:0;margin:0 auto;max-width:62.5rem;right:0;width:30%}}@media only screen and (min-width: 40.0625em){.reveal-modal.small{left:0;margin:0 auto;max-width:62.5rem;right:0;width:40%}}@media only screen and (min-width: 40.0625em){.reveal-modal.medium{left:0;margin:0 auto;max-width:62.5rem;right:0;width:60%}}@media only screen and (min-width: 40.0625em){.reveal-modal.large{left:0;margin:0 auto;max-width:62.5rem;right:0;width:70%}}@media only screen and (min-width: 40.0625em){.reveal-modal.xlarge{left:0;margin:0 auto;max-width:62.5rem;right:0;width:95%}}.reveal-modal.full{height:100vh;height:100%;left:0;margin-left:0 !important;max-width:none !important;min-height:100vh;top:0}@media only screen and (min-width: 40.0625em){.reveal-modal.full{left:0;margin:0 auto;max-width:62.5rem;right:0;width:100%}}.reveal-modal.toback{z-index:1003}.reveal-modal .close-reveal-modal{color:#aaa;cursor:pointer;font-size:2.5rem;font-weight:bold;line-height:1;position:absolute;top:0.625rem;right:1.375rem}.has-tip{border-bottom:dotted 1px #ccc;color:#333;cursor:help;font-weight:bold}.has-tip:hover,.has-tip:focus{border-bottom:dotted 1px #003f54;color:#008CBA}.has-tip.tip-left,.has-tip.tip-right{float:none !important}.tooltip{background:#333;color:#fff;display:none;font-size:0.875rem;font-weight:normal;line-height:1.3;max-width:300px;padding:0.75rem;position:absolute;width:100%;z-index:1006;left:50%}.tooltip>.nub{border-color:transparent transparent #333 transparent;border:solid 5px;display:block;height:0;pointer-events:none;position:absolute;top:-10px;width:0;left:5px}.tooltip>.nub.rtl{left:auto;right:5px}.tooltip.radius{border-radius:3px}.tooltip.round{border-radius:1000px}.tooltip.round>.nub{left:2rem}.tooltip.opened{border-bottom:dotted 1px #003f54 !important;color:#008CBA !important}.tap-to-close{color:#777;display:block;font-size:0.625rem;font-weight:normal}@media only screen and (min-width: 40.0625em){.tooltip>.nub{border-color:transparent transparent #333 transparent;top:-10px}.tooltip.tip-top>.nub{border-color:#333 transparent transparent transparent;bottom:-10px;top:auto}.tooltip.tip-left,.tooltip.tip-right{float:none !important}.tooltip.tip-left>.nub{border-color:transparent transparent transparent #333;left:auto;margin-top:-5px;right:-10px;top:50%}.tooltip.tip-right>.nub{border-color:transparent #333 transparent transparent;left:-10px;margin-top:-5px;right:auto;top:50%}}.clearing-thumbs,[data-clearing]{list-style:none;margin-left:0;margin-bottom:0}.clearing-thumbs:before,.clearing-thumbs:after,[data-clearing]:before,[data-clearing]:after{content:" ";display:table}.clearing-thumbs:after,[data-clearing]:after{clear:both}.clearing-thumbs li,[data-clearing] li{float:left;margin-right:10px}.clearing-thumbs[class*="block-grid-"] li,[data-clearing][class*="block-grid-"] li{margin-right:0}.clearing-blackout{background:#333;height:100%;position:fixed;top:0;width:100%;z-index:998;left:0}.clearing-blackout .clearing-close{display:block}.clearing-container{height:100%;margin:0;overflow:hidden;position:relative;z-index:998}.clearing-touch-label{color:#aaa;font-size:.6em;left:50%;position:absolute;top:50%}.visible-img{height:95%;position:relative}.visible-img img{position:absolute;left:50%;top:50%;-webkit-transform:translateY(-50%) translateX(-50%);-moz-transform:translateY(-50%) translateX(-50%);-ms-transform:translateY(-50%) translateX(-50%);-o-transform:translateY(-50%) translateX(-50%);transform:translateY(-50%) translateX(-50%);max-height:100%;max-width:100%}.clearing-caption{background:#333;bottom:0;color:#ccc;font-size:0.875em;line-height:1.3;margin-bottom:0;padding:10px 30px 20px;position:absolute;text-align:center;width:100%;left:0}.clearing-close{color:#ccc;display:none;font-size:30px;line-height:1;padding-left:20px;padding-top:10px;z-index:999}.clearing-close:hover,.clearing-close:focus{color:#ccc}.clearing-assembled .clearing-container{height:100%}.clearing-assembled .clearing-container .carousel>ul{display:none}.clearing-feature li{display:none}.clearing-feature li.clearing-featured-img{display:block}@media only screen and (min-width: 40.0625em){.clearing-main-prev,.clearing-main-next{height:100%;position:absolute;top:0;width:40px}.clearing-main-prev>span,.clearing-main-next>span{border:solid 12px;display:block;height:0;position:absolute;top:50%;width:0}.clearing-main-prev>span:hover,.clearing-main-next>span:hover{opacity:.8}.clearing-main-prev{left:0}.clearing-main-prev>span{left:5px;border-color:transparent;border-right-color:#ccc}.clearing-main-next{right:0}.clearing-main-next>span{border-color:transparent;border-left-color:#ccc}.clearing-main-prev.disabled,.clearing-main-next.disabled{opacity:.3}.clearing-assembled .clearing-container .carousel{background:rgba(51,51,51,0.8);height:120px;margin-top:10px;text-align:center}.clearing-assembled .clearing-container .carousel>ul{display:inline-block;z-index:999;height:100%;position:relative;float:none}.clearing-assembled .clearing-container .carousel>ul li{clear:none;cursor:pointer;display:block;float:left;margin-right:0;min-height:inherit;opacity:.4;overflow:hidden;padding:0;position:relative;width:120px}.clearing-assembled .clearing-container .carousel>ul li.fix-height img{height:100%;max-width:none}.clearing-assembled .clearing-container .carousel>ul li a.th{border:none;box-shadow:none;display:block}.clearing-assembled .clearing-container .carousel>ul li img{cursor:pointer !important;width:100% !important}.clearing-assembled .clearing-container .carousel>ul li.visible{opacity:1}.clearing-assembled .clearing-container .carousel>ul li:hover{opacity:.8}.clearing-assembled .clearing-container .visible-img{background:#333;height:85%;overflow:hidden}.clearing-close{padding-left:0;padding-top:0;position:absolute;top:10px;right:20px}}.progress{background-color:#F6F6F6;border:1px solid #fff;height:1.5625rem;margin-bottom:0.625rem;padding:0.125rem}.progress .meter{background:#008CBA;display:block;height:100%}.progress.secondary .meter{background:#e7e7e7;display:block;height:100%}.progress.success .meter{background:#43AC6A;display:block;height:100%}.progress.alert .meter{background:#f04124;display:block;height:100%}.progress.radius{border-radius:3px}.progress.radius .meter{border-radius:2px}.progress.round{border-radius:1000px}.progress.round .meter{border-radius:999px}.sub-nav{display:block;margin:-0.25rem 0 1.125rem;overflow:hidden;padding-top:0.25rem;width:auto}.sub-nav dt{text-transform:uppercase}.sub-nav dt,.sub-nav dd,.sub-nav li{color:#999;float:left;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:0.875rem;font-weight:normal;margin-left:1rem;margin-bottom:0}.sub-nav dt a,.sub-nav dd a,.sub-nav li a{color:#999;padding:0.1875rem 1rem;text-decoration:none}.sub-nav dt a:hover,.sub-nav dd a:hover,.sub-nav li a:hover{color:#737373}.sub-nav dt.active a,.sub-nav dd.active a,.sub-nav li.active a{border-radius:3px;background:#008CBA;color:#fff;cursor:default;font-weight:normal;padding:0.1875rem 1rem}.sub-nav dt.active a:hover,.sub-nav dd.active a:hover,.sub-nav li.active a:hover{background:#0078a0}.joyride-list{display:none}.joyride-tip-guide{background:#333;color:#fff;display:none;font-family:inherit;font-weight:normal;position:absolute;top:0;width:95%;z-index:101;left:2.5%}.lt-ie9 .joyride-tip-guide{margin-left:-400px;max-width:800px;left:50%}.joyride-content-wrapper{padding:1.125rem 1.25rem 1.5rem;width:100%}.joyride-content-wrapper .button{margin-bottom:0 !important}.joyride-content-wrapper .joyride-prev-tip{margin-right:10px}.joyride-tip-guide .joyride-nub{border:10px solid #333;display:block;height:0;position:absolute;width:0;left:22px}.joyride-tip-guide .joyride-nub.top{border-color:#333;border-top-color:transparent !important;border-top-style:solid;border-left-color:transparent !important;border-right-color:transparent !important;top:-20px}.joyride-tip-guide .joyride-nub.bottom{border-color:#333 !important;border-bottom-color:transparent !important;border-bottom-style:solid;border-left-color:transparent !important;border-right-color:transparent !important;bottom:-20px}.joyride-tip-guide .joyride-nub.right{right:-20px}.joyride-tip-guide .joyride-nub.left{left:-20px}.joyride-tip-guide h1,.joyride-tip-guide h2,.joyride-tip-guide h3,.joyride-tip-guide h4,.joyride-tip-guide h5,.joyride-tip-guide h6{color:#fff;font-weight:bold;line-height:1.25;margin:0}.joyride-tip-guide p{font-size:0.875rem;line-height:1.3;margin:0 0 1.125rem 0}.joyride-timer-indicator-wrap{border:solid 1px #555;bottom:1rem;height:3px;position:absolute;width:50px;right:1.0625rem}.joyride-timer-indicator{background:#666;display:block;height:inherit;width:0}.joyride-close-tip{color:#777 !important;font-size:24px;font-weight:normal;line-height:.5 !important;position:absolute;text-decoration:none;top:10px;right:12px}.joyride-close-tip:hover,.joyride-close-tip:focus{color:#eee !important}.joyride-modal-bg{background:rgba(0,0,0,0.5);cursor:pointer;display:none;height:100%;position:fixed;top:0;width:100%;z-index:100;left:0}.joyride-expose-wrapper{background-color:#fff;border-radius:3px;box-shadow:0 0 15px #fff;position:absolute;z-index:102}.joyride-expose-cover{background:transparent;border-radius:3px;left:0;position:absolute;top:0;z-index:9999}@media only screen and (min-width: 40.0625em){.joyride-tip-guide{width:300px;left:inherit}.joyride-tip-guide .joyride-nub.bottom{border-color:#333 !important;border-bottom-color:transparent !important;border-left-color:transparent !important;border-right-color:transparent !important;bottom:-20px}.joyride-tip-guide .joyride-nub.right{border-color:#333 !important;border-right-color:transparent !important;border-bottom-color:transparent !important;border-top-color:transparent !important;left:auto;right:-20px;top:22px}.joyride-tip-guide .joyride-nub.left{border-color:#333 !important;border-bottom-color:transparent !important;border-left-color:transparent !important;border-top-color:transparent !important;left:-20px;right:auto;top:22px}}.label{display:inline-block;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-weight:normal;line-height:1;margin-bottom:auto;position:relative;text-align:center;text-decoration:none;white-space:nowrap;padding:0.25rem 0.5rem 0.25rem;font-size:0.6875rem;background-color:#008CBA;color:#fff}.label.radius{border-radius:3px}.label.round{border-radius:1000px}.label.alert{background-color:#f04124;color:#fff}.label.warning{background-color:#f08a24;color:#fff}.label.success{background-color:#43AC6A;color:#fff}.label.secondary{background-color:#e7e7e7;color:#333}.label.info{background-color:#a0d3e8;color:#333}.off-canvas-wrap{-webkit-backface-visibility:hidden;position:relative;width:100%;overflow:hidden}.off-canvas-wrap.move-right,.off-canvas-wrap.move-left{min-height:100%;-webkit-overflow-scrolling:touch}.inner-wrap{position:relative;width:100%;-webkit-transition:-webkit-transform 500ms ease;-moz-transition:-moz-transform 500ms ease;-ms-transition:-ms-transform 500ms ease;-o-transition:-o-transform 500ms ease;transition:transform 500ms ease}.inner-wrap:before,.inner-wrap:after{content:" ";display:table}.inner-wrap:after{clear:both}.tab-bar{-webkit-backface-visibility:hidden;background:#333;color:#fff;height:2.8125rem;line-height:2.8125rem;position:relative}.tab-bar h1,.tab-bar h2,.tab-bar h3,.tab-bar h4,.tab-bar h5,.tab-bar h6{color:#fff;font-weight:bold;line-height:2.8125rem;margin:0}.tab-bar h1,.tab-bar h2,.tab-bar h3,.tab-bar h4{font-size:1.125rem}.left-small{height:2.8125rem;position:absolute;top:0;width:2.8125rem;border-right:solid 1px #1a1a1a;left:0}.right-small{height:2.8125rem;position:absolute;top:0;width:2.8125rem;border-left:solid 1px #1a1a1a;right:0}.tab-bar-section{height:2.8125rem;padding:0 0.625rem;position:absolute;text-align:center;top:0}.tab-bar-section.left{text-align:left}.tab-bar-section.right{text-align:right}.tab-bar-section.left{left:0;right:2.8125rem}.tab-bar-section.right{left:2.8125rem;right:0}.tab-bar-section.middle{left:2.8125rem;right:2.8125rem}.tab-bar .menu-icon{color:#fff;display:block;height:2.8125rem;padding:0;position:relative;text-indent:2.1875rem;transform:translate3d(0, 0, 0);width:2.8125rem}.tab-bar .menu-icon span::after{content:"";display:block;height:0;position:absolute;top:50%;margin-top:-0.5rem;left:0.90625rem;box-shadow:0 0 0 1px #fff,0 7px 0 1px #fff,0 14px 0 1px #fff;width:1rem}.tab-bar .menu-icon span:hover:after{box-shadow:0 0 0 1px #b3b3b3,0 7px 0 1px #b3b3b3,0 14px 0 1px #b3b3b3}.left-off-canvas-menu{-webkit-backface-visibility:hidden;background:#333;bottom:0;box-sizing:content-box;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;overflow-x:hidden;overflow-y:auto;position:absolute;top:0;transition:transform 500ms ease 0s;width:15.625rem;z-index:1001;-webkit-transform:translate3d(-100%, 0, 0);-moz-transform:translate3d(-100%, 0, 0);-ms-transform:translate(-100%, 0);-ms-transform:translate3d(-100%, 0, 0);-o-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.left-off-canvas-menu *{-webkit-backface-visibility:hidden}.right-off-canvas-menu{-webkit-backface-visibility:hidden;background:#333;bottom:0;box-sizing:content-box;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;overflow-x:hidden;overflow-y:auto;position:absolute;top:0;transition:transform 500ms ease 0s;width:15.625rem;z-index:1001;-webkit-transform:translate3d(100%, 0, 0);-moz-transform:translate3d(100%, 0, 0);-ms-transform:translate(100%, 0);-ms-transform:translate3d(100%, 0, 0);-o-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);right:0}.right-off-canvas-menu *{-webkit-backface-visibility:hidden}ul.off-canvas-list{list-style-type:none;margin:0;padding:0}ul.off-canvas-list li label{background:#444;border-bottom:none;border-top:1px solid #5e5e5e;color:#999;display:block;font-size:0.75rem;font-weight:bold;margin:0;padding:0.3rem 0.9375rem;text-transform:uppercase}ul.off-canvas-list li a{border-bottom:1px solid #262626;color:rgba(255,255,255,0.7);display:block;padding:0.66667rem;transition:background 300ms ease}ul.off-canvas-list li a:hover{background:#242424}ul.off-canvas-list li a:active{background:#242424}.move-right>.inner-wrap{-webkit-transform:translate3d(15.625rem, 0, 0);-moz-transform:translate3d(15.625rem, 0, 0);-ms-transform:translate(15.625rem, 0);-ms-transform:translate3d(15.625rem, 0, 0);-o-transform:translate3d(15.625rem, 0, 0);transform:translate3d(15.625rem, 0, 0)}.move-right .exit-off-canvas{-webkit-backface-visibility:hidden;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);cursor:pointer;transition:background 300ms ease;-webkit-tap-highlight-color:transparent;background:rgba(255,255,255,0.2);bottom:0;display:block;left:0;position:absolute;right:0;top:0;z-index:1002}@media only screen and (min-width: 40.0625em){.move-right .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.move-left>.inner-wrap{-webkit-transform:translate3d(-15.625rem, 0, 0);-moz-transform:translate3d(-15.625rem, 0, 0);-ms-transform:translate(-15.625rem, 0);-ms-transform:translate3d(-15.625rem, 0, 0);-o-transform:translate3d(-15.625rem, 0, 0);transform:translate3d(-15.625rem, 0, 0)}.move-left .exit-off-canvas{-webkit-backface-visibility:hidden;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);cursor:pointer;transition:background 300ms ease;-webkit-tap-highlight-color:transparent;background:rgba(255,255,255,0.2);bottom:0;display:block;left:0;position:absolute;right:0;top:0;z-index:1002}@media only screen and (min-width: 40.0625em){.move-left .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.offcanvas-overlap .left-off-canvas-menu,.offcanvas-overlap .right-off-canvas-menu{-ms-transform:none;-webkit-transform:none;-moz-transform:none;-o-transform:none;transform:none;z-index:1003}.offcanvas-overlap .exit-off-canvas{-webkit-backface-visibility:hidden;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);cursor:pointer;transition:background 300ms ease;-webkit-tap-highlight-color:transparent;background:rgba(255,255,255,0.2);bottom:0;display:block;left:0;position:absolute;right:0;top:0;z-index:1002}@media only screen and (min-width: 40.0625em){.offcanvas-overlap .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.offcanvas-overlap-left .right-off-canvas-menu{-ms-transform:none;-webkit-transform:none;-moz-transform:none;-o-transform:none;transform:none;z-index:1003}.offcanvas-overlap-left .exit-off-canvas{-webkit-backface-visibility:hidden;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);cursor:pointer;transition:background 300ms ease;-webkit-tap-highlight-color:transparent;background:rgba(255,255,255,0.2);bottom:0;display:block;left:0;position:absolute;right:0;top:0;z-index:1002}@media only screen and (min-width: 40.0625em){.offcanvas-overlap-left .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.offcanvas-overlap-right .left-off-canvas-menu{-ms-transform:none;-webkit-transform:none;-moz-transform:none;-o-transform:none;transform:none;z-index:1003}.offcanvas-overlap-right .exit-off-canvas{-webkit-backface-visibility:hidden;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);cursor:pointer;transition:background 300ms ease;-webkit-tap-highlight-color:transparent;background:rgba(255,255,255,0.2);bottom:0;display:block;left:0;position:absolute;right:0;top:0;z-index:1002}@media only screen and (min-width: 40.0625em){.offcanvas-overlap-right .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.no-csstransforms .left-off-canvas-menu{left:-15.625rem}.no-csstransforms .right-off-canvas-menu{right:-15.625rem}.no-csstransforms .move-left>.inner-wrap{right:15.625rem}.no-csstransforms .move-right>.inner-wrap{left:15.625rem}.left-submenu{-webkit-backface-visibility:hidden;-webkit-overflow-scrolling:touch;background:#333;bottom:0;box-sizing:content-box;margin:0;overflow-x:hidden;overflow-y:auto;position:absolute;top:0;width:15.625rem;z-index:1002;-webkit-transform:translate3d(-100%, 0, 0);-moz-transform:translate3d(-100%, 0, 0);-ms-transform:translate(-100%, 0);-ms-transform:translate3d(-100%, 0, 0);-o-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0;-webkit-transition:-webkit-transform 500ms ease;-moz-transition:-moz-transform 500ms ease;-ms-transition:-ms-transform 500ms ease;-o-transition:-o-transform 500ms ease;transition:transform 500ms ease}.left-submenu *{-webkit-backface-visibility:hidden}.left-submenu .back>a{background:#444;border-bottom:none;border-top:1px solid #5e5e5e;color:#999;font-weight:bold;padding:0.3rem 0.9375rem;text-transform:uppercase;margin:0}.left-submenu .back>a:hover{background:#303030;border-bottom:none;border-top:1px solid #5e5e5e}.left-submenu .back>a:before{content:"\AB";margin-right:.5rem;display:inline}.left-submenu.move-right,.left-submenu.offcanvas-overlap-right,.left-submenu.offcanvas-overlap{-webkit-transform:translate3d(0%, 0, 0);-moz-transform:translate3d(0%, 0, 0);-ms-transform:translate(0%, 0);-ms-transform:translate3d(0%, 0, 0);-o-transform:translate3d(0%, 0, 0);transform:translate3d(0%, 0, 0)}.right-submenu{-webkit-backface-visibility:hidden;-webkit-overflow-scrolling:touch;background:#333;bottom:0;box-sizing:content-box;margin:0;overflow-x:hidden;overflow-y:auto;position:absolute;top:0;width:15.625rem;z-index:1002;-webkit-transform:translate3d(100%, 0, 0);-moz-transform:translate3d(100%, 0, 0);-ms-transform:translate(100%, 0);-ms-transform:translate3d(100%, 0, 0);-o-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);right:0;-webkit-transition:-webkit-transform 500ms ease;-moz-transition:-moz-transform 500ms ease;-ms-transition:-ms-transform 500ms ease;-o-transition:-o-transform 500ms ease;transition:transform 500ms ease}.right-submenu *{-webkit-backface-visibility:hidden}.right-submenu .back>a{background:#444;border-bottom:none;border-top:1px solid #5e5e5e;color:#999;font-weight:bold;padding:0.3rem 0.9375rem;text-transform:uppercase;margin:0}.right-submenu .back>a:hover{background:#303030;border-bottom:none;border-top:1px solid #5e5e5e}.right-submenu .back>a:after{content:"\BB";margin-left:.5rem;display:inline}.right-submenu.move-left,.right-submenu.offcanvas-overlap-left,.right-submenu.offcanvas-overlap{-webkit-transform:translate3d(0%, 0, 0);-moz-transform:translate3d(0%, 0, 0);-ms-transform:translate(0%, 0);-ms-transform:translate3d(0%, 0, 0);-o-transform:translate3d(0%, 0, 0);transform:translate3d(0%, 0, 0)}.left-off-canvas-menu ul.off-canvas-list li.has-submenu>a:after{content:"\BB";margin-left:.5rem;display:inline}.right-off-canvas-menu ul.off-canvas-list li.has-submenu>a:before{content:"\AB";margin-right:.5rem;display:inline}.f-dropdown{display:none;left:-9999px;list-style:none;margin-left:0;position:absolute;background:#fff;border:solid 1px #ccc;font-size:0.875rem;height:auto;max-height:none;width:100%;z-index:89;margin-top:2px;max-width:200px}.f-dropdown.open{display:block}.f-dropdown>*:first-child{margin-top:0}.f-dropdown>*:last-child{margin-bottom:0}.f-dropdown:before{border:inset 6px;content:"";display:block;height:0;width:0;border-color:transparent transparent #fff transparent;border-bottom-style:solid;position:absolute;top:-12px;left:10px;z-index:89}.f-dropdown:after{border:inset 7px;content:"";display:block;height:0;width:0;border-color:transparent transparent #ccc transparent;border-bottom-style:solid;position:absolute;top:-14px;left:9px;z-index:88}.f-dropdown.right:before{left:auto;right:10px}.f-dropdown.right:after{left:auto;right:9px}.f-dropdown.drop-right{display:none;left:-9999px;list-style:none;margin-left:0;position:absolute;background:#fff;border:solid 1px #ccc;font-size:0.875rem;height:auto;max-height:none;width:100%;z-index:89;margin-top:0;margin-left:2px;max-width:200px}.f-dropdown.drop-right.open{display:block}.f-dropdown.drop-right>*:first-child{margin-top:0}.f-dropdown.drop-right>*:last-child{margin-bottom:0}.f-dropdown.drop-right:before{border:inset 6px;content:"";display:block;height:0;width:0;border-color:transparent #fff transparent transparent;border-right-style:solid;position:absolute;top:10px;left:-12px;z-index:89}.f-dropdown.drop-right:after{border:inset 7px;content:"";display:block;height:0;width:0;border-color:transparent #ccc transparent transparent;border-right-style:solid;position:absolute;top:9px;left:-14px;z-index:88}.f-dropdown.drop-left{display:none;left:-9999px;list-style:none;margin-left:0;position:absolute;background:#fff;border:solid 1px #ccc;font-size:0.875rem;height:auto;max-height:none;width:100%;z-index:89;margin-top:0;margin-left:-2px;max-width:200px}.f-dropdown.drop-left.open{display:block}.f-dropdown.drop-left>*:first-child{margin-top:0}.f-dropdown.drop-left>*:last-child{margin-bottom:0}.f-dropdown.drop-left:before{border:inset 6px;content:"";display:block;height:0;width:0;border-color:transparent transparent transparent #fff;border-left-style:solid;position:absolute;top:10px;right:-12px;left:auto;z-index:89}.f-dropdown.drop-left:after{border:inset 7px;content:"";display:block;height:0;width:0;border-color:transparent transparent transparent #ccc;border-left-style:solid;position:absolute;top:9px;right:-14px;left:auto;z-index:88}.f-dropdown.drop-top{display:none;left:-9999px;list-style:none;margin-left:0;position:absolute;background:#fff;border:solid 1px #ccc;font-size:0.875rem;height:auto;max-height:none;width:100%;z-index:89;margin-left:0;margin-top:-2px;max-width:200px}.f-dropdown.drop-top.open{display:block}.f-dropdown.drop-top>*:first-child{margin-top:0}.f-dropdown.drop-top>*:last-child{margin-bottom:0}.f-dropdown.drop-top:before{border:inset 6px;content:"";display:block;height:0;width:0;border-color:#fff transparent transparent transparent;border-top-style:solid;bottom:-12px;position:absolute;top:auto;left:10px;right:auto;z-index:89}.f-dropdown.drop-top:after{border:inset 7px;content:"";display:block;height:0;width:0;border-color:#ccc transparent transparent transparent;border-top-style:solid;bottom:-14px;position:absolute;top:auto;left:9px;right:auto;z-index:88}.f-dropdown li{cursor:pointer;font-size:0.875rem;line-height:1.125rem;margin:0}.f-dropdown li:hover,.f-dropdown li:focus{background:#eee}.f-dropdown li.radius{border-radius:3px}.f-dropdown li a{display:block;padding:0.5rem;color:#555}.f-dropdown.content{display:none;left:-9999px;list-style:none;margin-left:0;position:absolute;background:#fff;border:solid 1px #ccc;font-size:0.875rem;height:auto;max-height:none;padding:1.25rem;width:100%;z-index:89;max-width:200px}.f-dropdown.content.open{display:block}.f-dropdown.content>*:first-child{margin-top:0}.f-dropdown.content>*:last-child{margin-bottom:0}.f-dropdown.tiny{max-width:200px}.f-dropdown.small{max-width:300px}.f-dropdown.medium{max-width:500px}.f-dropdown.large{max-width:800px}.f-dropdown.mega{width:100% !important;max-width:100% !important}.f-dropdown.mega.open{left:0 !important}table{background:#fff;border:solid 1px #ddd;margin-bottom:1.25rem;table-layout:auto}table caption{background:transparent;color:#222;font-size:1rem;font-weight:bold}table thead{background:#F5F5F5}table thead tr th,table thead tr td{color:#222;font-size:0.875rem;font-weight:bold;padding:0.5rem 0.625rem 0.625rem}table tfoot{background:#F5F5F5}table tfoot tr th,table tfoot tr td{color:#222;font-size:0.875rem;font-weight:bold;padding:0.5rem 0.625rem 0.625rem}table tr th,table tr td{color:#222;font-size:0.875rem;padding:0.5625rem 0.625rem;text-align:left}table tr.even,table tr.alt,table tr:nth-of-type(even){background:#F9F9F9}table thead tr th,table tfoot tr th,table tfoot tr td,table tbody tr th,table tbody tr td,table tr td{display:table-cell;line-height:1.125rem}.range-slider{border:1px solid #ddd;margin:1.25rem 0;position:relative;-ms-touch-action:none;touch-action:none;display:block;height:1rem;width:100%;background:#FAFAFA}.range-slider.vertical-range{border:1px solid #ddd;margin:1.25rem 0;position:relative;-ms-touch-action:none;touch-action:none;display:inline-block;height:12.5rem;width:1rem}.range-slider.vertical-range .range-slider-handle{bottom:-10.5rem;margin-left:-0.5rem;margin-top:0;position:absolute}.range-slider.vertical-range .range-slider-active-segment{border-bottom-left-radius:inherit;border-bottom-right-radius:inherit;border-top-left-radius:initial;bottom:0;height:auto;width:0.875rem}.range-slider.radius{background:#FAFAFA;border-radius:3px}.range-slider.radius .range-slider-handle{background:#008CBA;border-radius:3px}.range-slider.radius .range-slider-handle:hover{background:#007ba4}.range-slider.round{background:#FAFAFA;border-radius:1000px}.range-slider.round .range-slider-handle{background:#008CBA;border-radius:1000px}.range-slider.round .range-slider-handle:hover{background:#007ba4}.range-slider.disabled,.range-slider[disabled]{background:#FAFAFA;cursor:not-allowed;opacity:0.7}.range-slider.disabled .range-slider-handle,.range-slider[disabled] .range-slider-handle{background:#008CBA;cursor:default;opacity:0.7}.range-slider.disabled .range-slider-handle:hover,.range-slider[disabled] .range-slider-handle:hover{background:#007ba4}.range-slider-active-segment{background:#e5e5e5;border-bottom-left-radius:inherit;border-top-left-radius:inherit;display:inline-block;height:0.875rem;position:absolute}.range-slider-handle{border:1px solid none;cursor:pointer;display:inline-block;height:1.375rem;position:absolute;top:-0.3125rem;width:2rem;z-index:1;-ms-touch-action:manipulation;touch-action:manipulation;background:#008CBA}.range-slider-handle:hover{background:#007ba4}[class*="block-grid-"]{display:block;padding:0;margin:0 -0.625rem}[class*="block-grid-"]:before,[class*="block-grid-"]:after{content:" ";display:table}[class*="block-grid-"]:after{clear:both}[class*="block-grid-"]>li{display:block;float:left;height:auto;padding:0 0.625rem 1.25rem}@media only screen{.small-block-grid-1>li{list-style:none;width:100%}.small-block-grid-1>li:nth-of-type(1n){clear:none}.small-block-grid-1>li:nth-of-type(1n+1){clear:both}.small-block-grid-2>li{list-style:none;width:50%}.small-block-grid-2>li:nth-of-type(1n){clear:none}.small-block-grid-2>li:nth-of-type(2n+1){clear:both}.small-block-grid-3>li{list-style:none;width:33.33333%}.small-block-grid-3>li:nth-of-type(1n){clear:none}.small-block-grid-3>li:nth-of-type(3n+1){clear:both}.small-block-grid-4>li{list-style:none;width:25%}.small-block-grid-4>li:nth-of-type(1n){clear:none}.small-block-grid-4>li:nth-of-type(4n+1){clear:both}.small-block-grid-5>li{list-style:none;width:20%}.small-block-grid-5>li:nth-of-type(1n){clear:none}.small-block-grid-5>li:nth-of-type(5n+1){clear:both}.small-block-grid-6>li{list-style:none;width:16.66667%}.small-block-grid-6>li:nth-of-type(1n){clear:none}.small-block-grid-6>li:nth-of-type(6n+1){clear:both}.small-block-grid-7>li{list-style:none;width:14.28571%}.small-block-grid-7>li:nth-of-type(1n){clear:none}.small-block-grid-7>li:nth-of-type(7n+1){clear:both}.small-block-grid-8>li{list-style:none;width:12.5%}.small-block-grid-8>li:nth-of-type(1n){clear:none}.small-block-grid-8>li:nth-of-type(8n+1){clear:both}.small-block-grid-9>li{list-style:none;width:11.11111%}.small-block-grid-9>li:nth-of-type(1n){clear:none}.small-block-grid-9>li:nth-of-type(9n+1){clear:both}.small-block-grid-10>li{list-style:none;width:10%}.small-block-grid-10>li:nth-of-type(1n){clear:none}.small-block-grid-10>li:nth-of-type(10n+1){clear:both}.small-block-grid-11>li{list-style:none;width:9.09091%}.small-block-grid-11>li:nth-of-type(1n){clear:none}.small-block-grid-11>li:nth-of-type(11n+1){clear:both}.small-block-grid-12>li{list-style:none;width:8.33333%}.small-block-grid-12>li:nth-of-type(1n){clear:none}.small-block-grid-12>li:nth-of-type(12n+1){clear:both}}@media only screen and (min-width: 40.0625em){.medium-block-grid-1>li{list-style:none;width:100%}.medium-block-grid-1>li:nth-of-type(1n){clear:none}.medium-block-grid-1>li:nth-of-type(1n+1){clear:both}.medium-block-grid-2>li{list-style:none;width:50%}.medium-block-grid-2>li:nth-of-type(1n){clear:none}.medium-block-grid-2>li:nth-of-type(2n+1){clear:both}.medium-block-grid-3>li{list-style:none;width:33.33333%}.medium-block-grid-3>li:nth-of-type(1n){clear:none}.medium-block-grid-3>li:nth-of-type(3n+1){clear:both}.medium-block-grid-4>li{list-style:none;width:25%}.medium-block-grid-4>li:nth-of-type(1n){clear:none}.medium-block-grid-4>li:nth-of-type(4n+1){clear:both}.medium-block-grid-5>li{list-style:none;width:20%}.medium-block-grid-5>li:nth-of-type(1n){clear:none}.medium-block-grid-5>li:nth-of-type(5n+1){clear:both}.medium-block-grid-6>li{list-style:none;width:16.66667%}.medium-block-grid-6>li:nth-of-type(1n){clear:none}.medium-block-grid-6>li:nth-of-type(6n+1){clear:both}.medium-block-grid-7>li{list-style:none;width:14.28571%}.medium-block-grid-7>li:nth-of-type(1n){clear:none}.medium-block-grid-7>li:nth-of-type(7n+1){clear:both}.medium-block-grid-8>li{list-style:none;width:12.5%}.medium-block-grid-8>li:nth-of-type(1n){clear:none}.medium-block-grid-8>li:nth-of-type(8n+1){clear:both}.medium-block-grid-9>li{list-style:none;width:11.11111%}.medium-block-grid-9>li:nth-of-type(1n){clear:none}.medium-block-grid-9>li:nth-of-type(9n+1){clear:both}.medium-block-grid-10>li{list-style:none;width:10%}.medium-block-grid-10>li:nth-of-type(1n){clear:none}.medium-block-grid-10>li:nth-of-type(10n+1){clear:both}.medium-block-grid-11>li{list-style:none;width:9.09091%}.medium-block-grid-11>li:nth-of-type(1n){clear:none}.medium-block-grid-11>li:nth-of-type(11n+1){clear:both}.medium-block-grid-12>li{list-style:none;width:8.33333%}.medium-block-grid-12>li:nth-of-type(1n){clear:none}.medium-block-grid-12>li:nth-of-type(12n+1){clear:both}}@media only screen and (min-width: 64.0625em){.large-block-grid-1>li{list-style:none;width:100%}.large-block-grid-1>li:nth-of-type(1n){clear:none}.large-block-grid-1>li:nth-of-type(1n+1){clear:both}.large-block-grid-2>li{list-style:none;width:50%}.large-block-grid-2>li:nth-of-type(1n){clear:none}.large-block-grid-2>li:nth-of-type(2n+1){clear:both}.large-block-grid-3>li{list-style:none;width:33.33333%}.large-block-grid-3>li:nth-of-type(1n){clear:none}.large-block-grid-3>li:nth-of-type(3n+1){clear:both}.large-block-grid-4>li{list-style:none;width:25%}.large-block-grid-4>li:nth-of-type(1n){clear:none}.large-block-grid-4>li:nth-of-type(4n+1){clear:both}.large-block-grid-5>li{list-style:none;width:20%}.large-block-grid-5>li:nth-of-type(1n){clear:none}.large-block-grid-5>li:nth-of-type(5n+1){clear:both}.large-block-grid-6>li{list-style:none;width:16.66667%}.large-block-grid-6>li:nth-of-type(1n){clear:none}.large-block-grid-6>li:nth-of-type(6n+1){clear:both}.large-block-grid-7>li{list-style:none;width:14.28571%}.large-block-grid-7>li:nth-of-type(1n){clear:none}.large-block-grid-7>li:nth-of-type(7n+1){clear:both}.large-block-grid-8>li{list-style:none;width:12.5%}.large-block-grid-8>li:nth-of-type(1n){clear:none}.large-block-grid-8>li:nth-of-type(8n+1){clear:both}.large-block-grid-9>li{list-style:none;width:11.11111%}.large-block-grid-9>li:nth-of-type(1n){clear:none}.large-block-grid-9>li:nth-of-type(9n+1){clear:both}.large-block-grid-10>li{list-style:none;width:10%}.large-block-grid-10>li:nth-of-type(1n){clear:none}.large-block-grid-10>li:nth-of-type(10n+1){clear:both}.large-block-grid-11>li{list-style:none;width:9.09091%}.large-block-grid-11>li:nth-of-type(1n){clear:none}.large-block-grid-11>li:nth-of-type(11n+1){clear:both}.large-block-grid-12>li{list-style:none;width:8.33333%}.large-block-grid-12>li:nth-of-type(1n){clear:none}.large-block-grid-12>li:nth-of-type(12n+1){clear:both}}.flex-video{height:0;margin-bottom:1rem;overflow:hidden;padding-bottom:67.5%;padding-top:1.5625rem;position:relative}.flex-video.widescreen{padding-bottom:56.34%}.flex-video.vimeo{padding-top:0}.flex-video iframe,.flex-video object,.flex-video embed,.flex-video video{height:100%;position:absolute;top:0;width:100%;left:0}.keystroke,kbd{background-color:#ededed;border-color:#ddd;color:#222;border-style:solid;border-width:1px;font-family:"Consolas","Menlo","Courier",monospace;font-size:inherit;margin:0;padding:0.125rem 0.25rem 0;border-radius:3px}.switch{border:none;margin-bottom:1.5rem;outline:0;padding:0;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.switch label{background:#ddd;color:transparent;cursor:pointer;display:block;margin-bottom:1rem;position:relative;text-indent:100%;width:4rem;height:2rem;transition:left 0.15s ease-out}.switch input{left:10px;opacity:0;padding:0;position:absolute;top:9px}.switch input+label{margin-left:0;margin-right:0}.switch label:after{background:#fff;content:"";display:block;height:1.5rem;left:.25rem;position:absolute;top:.25rem;width:1.5rem;-webkit-transition:left 0.15s ease-out;-moz-transition:left 0.15s ease-out;-o-transition:translate3d(0, 0, 0);transition:left 0.15s ease-out;-webkit-transform:translate3d(0, 0, 0);-moz-transform:translate3d(0, 0, 0);-ms-transform:translate3d(0, 0, 0);-o-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}.switch input:checked+label{background:#008CBA}.switch input:checked+label:after{left:2.25rem}.switch label{height:2rem;width:4rem}.switch label:after{height:1.5rem;width:1.5rem}.switch input:checked+label:after{left:2.25rem}.switch label{color:transparent;background:#ddd}.switch label:after{background:#fff}.switch input:checked+label{background:#008CBA}.switch.large label{height:2.5rem;width:5rem}.switch.large label:after{height:2rem;width:2rem}.switch.large input:checked+label:after{left:2.75rem}.switch.small label{height:1.75rem;width:3.5rem}.switch.small label:after{height:1.25rem;width:1.25rem}.switch.small input:checked+label:after{left:2rem}.switch.tiny label{height:1.5rem;width:3rem}.switch.tiny label:after{height:1rem;width:1rem}.switch.tiny input:checked+label:after{left:1.75rem}.switch.radius label{border-radius:4px}.switch.radius label:after{border-radius:3px}.switch.round{border-radius:1000px}.switch.round label{border-radius:2rem}.switch.round label:after{border-radius:2rem}@media only screen{.show-for-small-only,.show-for-small-up,.show-for-small,.show-for-small-down,.hide-for-medium-only,.hide-for-medium-up,.hide-for-medium,.show-for-medium-down,.hide-for-large-only,.hide-for-large-up,.hide-for-large,.show-for-large-down,.hide-for-xlarge-only,.hide-for-xlarge-up,.hide-for-xlarge,.show-for-xlarge-down,.hide-for-xxlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge,.show-for-xxlarge-down{display:inherit !important}.hide-for-small-only,.hide-for-small-up,.hide-for-small,.hide-for-small-down,.show-for-medium-only,.show-for-medium-up,.show-for-medium,.hide-for-medium-down,.show-for-large-only,.show-for-large-up,.show-for-large,.hide-for-large-down,.show-for-xlarge-only,.show-for-xlarge-up,.show-for-xlarge,.hide-for-xlarge-down,.show-for-xxlarge-only,.show-for-xxlarge-up,.show-for-xxlarge,.hide-for-xxlarge-down{display:none !important}.visible-for-small-only,.visible-for-small-up,.visible-for-small,.visible-for-small-down,.hidden-for-medium-only,.hidden-for-medium-up,.hidden-for-medium,.visible-for-medium-down,.hidden-for-large-only,.hidden-for-large-up,.hidden-for-large,.visible-for-large-down,.hidden-for-xlarge-only,.hidden-for-xlarge-up,.hidden-for-xlarge,.visible-for-xlarge-down,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up,.hidden-for-xxlarge,.visible-for-xxlarge-down{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.hidden-for-small-only,.hidden-for-small-up,.hidden-for-small,.hidden-for-small-down,.visible-for-medium-only,.visible-for-medium-up,.visible-for-medium,.hidden-for-medium-down,.visible-for-large-only,.visible-for-large-up,.visible-for-large,.hidden-for-large-down,.visible-for-xlarge-only,.visible-for-xlarge-up,.visible-for-xlarge,.hidden-for-xlarge-down,.visible-for-xxlarge-only,.visible-for-xxlarge-up,.visible-for-xxlarge,.hidden-for-xxlarge-down{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px}table.show-for-small-only,table.show-for-small-up,table.show-for-small,table.show-for-small-down,table.hide-for-medium-only,table.hide-for-medium-up,table.hide-for-medium,table.show-for-medium-down,table.hide-for-large-only,table.hide-for-large-up,table.hide-for-large,table.show-for-large-down,table.hide-for-xlarge-only,table.hide-for-xlarge-up,table.hide-for-xlarge,table.show-for-xlarge-down,table.hide-for-xxlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge,table.show-for-xxlarge-down{display:table !important}thead.show-for-small-only,thead.show-for-small-up,thead.show-for-small,thead.show-for-small-down,thead.hide-for-medium-only,thead.hide-for-medium-up,thead.hide-for-medium,thead.show-for-medium-down,thead.hide-for-large-only,thead.hide-for-large-up,thead.hide-for-large,thead.show-for-large-down,thead.hide-for-xlarge-only,thead.hide-for-xlarge-up,thead.hide-for-xlarge,thead.show-for-xlarge-down,thead.hide-for-xxlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge,thead.show-for-xxlarge-down{display:table-header-group !important}tbody.show-for-small-only,tbody.show-for-small-up,tbody.show-for-small,tbody.show-for-small-down,tbody.hide-for-medium-only,tbody.hide-for-medium-up,tbody.hide-for-medium,tbody.show-for-medium-down,tbody.hide-for-large-only,tbody.hide-for-large-up,tbody.hide-for-large,tbody.show-for-large-down,tbody.hide-for-xlarge-only,tbody.hide-for-xlarge-up,tbody.hide-for-xlarge,tbody.show-for-xlarge-down,tbody.hide-for-xxlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge,tbody.show-for-xxlarge-down{display:table-row-group !important}tr.show-for-small-only,tr.show-for-small-up,tr.show-for-small,tr.show-for-small-down,tr.hide-for-medium-only,tr.hide-for-medium-up,tr.hide-for-medium,tr.show-for-medium-down,tr.hide-for-large-only,tr.hide-for-large-up,tr.hide-for-large,tr.show-for-large-down,tr.hide-for-xlarge-only,tr.hide-for-xlarge-up,tr.hide-for-xlarge,tr.show-for-xlarge-down,tr.hide-for-xxlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge,tr.show-for-xxlarge-down{display:table-row}th.show-for-small-only,td.show-for-small-only,th.show-for-small-up,td.show-for-small-up,th.show-for-small,td.show-for-small,th.show-for-small-down,td.show-for-small-down,th.hide-for-medium-only,td.hide-for-medium-only,th.hide-for-medium-up,td.hide-for-medium-up,th.hide-for-medium,td.hide-for-medium,th.show-for-medium-down,td.show-for-medium-down,th.hide-for-large-only,td.hide-for-large-only,th.hide-for-large-up,td.hide-for-large-up,th.hide-for-large,td.hide-for-large,th.show-for-large-down,td.show-for-large-down,th.hide-for-xlarge-only,td.hide-for-xlarge-only,th.hide-for-xlarge-up,td.hide-for-xlarge-up,th.hide-for-xlarge,td.hide-for-xlarge,th.show-for-xlarge-down,td.show-for-xlarge-down,th.hide-for-xxlarge-only,td.hide-for-xxlarge-only,th.hide-for-xxlarge-up,td.hide-for-xxlarge-up,th.hide-for-xxlarge,td.hide-for-xxlarge,th.show-for-xxlarge-down,td.show-for-xxlarge-down{display:table-cell !important}}@media only screen and (min-width: 40.0625em){.hide-for-small-only,.show-for-small-up,.hide-for-small,.hide-for-small-down,.show-for-medium-only,.show-for-medium-up,.show-for-medium,.show-for-medium-down,.hide-for-large-only,.hide-for-large-up,.hide-for-large,.show-for-large-down,.hide-for-xlarge-only,.hide-for-xlarge-up,.hide-for-xlarge,.show-for-xlarge-down,.hide-for-xxlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge,.show-for-xxlarge-down{display:inherit !important}.show-for-small-only,.hide-for-small-up,.show-for-small,.show-for-small-down,.hide-for-medium-only,.hide-for-medium-up,.hide-for-medium,.hide-for-medium-down,.show-for-large-only,.show-for-large-up,.show-for-large,.hide-for-large-down,.show-for-xlarge-only,.show-for-xlarge-up,.show-for-xlarge,.hide-for-xlarge-down,.show-for-xxlarge-only,.show-for-xxlarge-up,.show-for-xxlarge,.hide-for-xxlarge-down{display:none !important}.hidden-for-small-only,.visible-for-small-up,.hidden-for-small,.hidden-for-small-down,.visible-for-medium-only,.visible-for-medium-up,.visible-for-medium,.visible-for-medium-down,.hidden-for-large-only,.hidden-for-large-up,.hidden-for-large,.visible-for-large-down,.hidden-for-xlarge-only,.hidden-for-xlarge-up,.hidden-for-xlarge,.visible-for-xlarge-down,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up,.hidden-for-xxlarge,.visible-for-xxlarge-down{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.visible-for-small-only,.hidden-for-small-up,.visible-for-small,.visible-for-small-down,.hidden-for-medium-only,.hidden-for-medium-up,.hidden-for-medium,.hidden-for-medium-down,.visible-for-large-only,.visible-for-large-up,.visible-for-large,.hidden-for-large-down,.visible-for-xlarge-only,.visible-for-xlarge-up,.visible-for-xlarge,.hidden-for-xlarge-down,.visible-for-xxlarge-only,.visible-for-xxlarge-up,.visible-for-xxlarge,.hidden-for-xxlarge-down{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px}table.hide-for-small-only,table.show-for-small-up,table.hide-for-small,table.hide-for-small-down,table.show-for-medium-only,table.show-for-medium-up,table.show-for-medium,table.show-for-medium-down,table.hide-for-large-only,table.hide-for-large-up,table.hide-for-large,table.show-for-large-down,table.hide-for-xlarge-only,table.hide-for-xlarge-up,table.hide-for-xlarge,table.show-for-xlarge-down,table.hide-for-xxlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge,table.show-for-xxlarge-down{display:table !important}thead.hide-for-small-only,thead.show-for-small-up,thead.hide-for-small,thead.hide-for-small-down,thead.show-for-medium-only,thead.show-for-medium-up,thead.show-for-medium,thead.show-for-medium-down,thead.hide-for-large-only,thead.hide-for-large-up,thead.hide-for-large,thead.show-for-large-down,thead.hide-for-xlarge-only,thead.hide-for-xlarge-up,thead.hide-for-xlarge,thead.show-for-xlarge-down,thead.hide-for-xxlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge,thead.show-for-xxlarge-down{display:table-header-group !important}tbody.hide-for-small-only,tbody.show-for-small-up,tbody.hide-for-small,tbody.hide-for-small-down,tbody.show-for-medium-only,tbody.show-for-medium-up,tbody.show-for-medium,tbody.show-for-medium-down,tbody.hide-for-large-only,tbody.hide-for-large-up,tbody.hide-for-large,tbody.show-for-large-down,tbody.hide-for-xlarge-only,tbody.hide-for-xlarge-up,tbody.hide-for-xlarge,tbody.show-for-xlarge-down,tbody.hide-for-xxlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge,tbody.show-for-xxlarge-down{display:table-row-group !important}tr.hide-for-small-only,tr.show-for-small-up,tr.hide-for-small,tr.hide-for-small-down,tr.show-for-medium-only,tr.show-for-medium-up,tr.show-for-medium,tr.show-for-medium-down,tr.hide-for-large-only,tr.hide-for-large-up,tr.hide-for-large,tr.show-for-large-down,tr.hide-for-xlarge-only,tr.hide-for-xlarge-up,tr.hide-for-xlarge,tr.show-for-xlarge-down,tr.hide-for-xxlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge,tr.show-for-xxlarge-down{display:table-row}th.hide-for-small-only,td.hide-for-small-only,th.show-for-small-up,td.show-for-small-up,th.hide-for-small,td.hide-for-small,th.hide-for-small-down,td.hide-for-small-down,th.show-for-medium-only,td.show-for-medium-only,th.show-for-medium-up,td.show-for-medium-up,th.show-for-medium,td.show-for-medium,th.show-for-medium-down,td.show-for-medium-down,th.hide-for-large-only,td.hide-for-large-only,th.hide-for-large-up,td.hide-for-large-up,th.hide-for-large,td.hide-for-large,th.show-for-large-down,td.show-for-large-down,th.hide-for-xlarge-only,td.hide-for-xlarge-only,th.hide-for-xlarge-up,td.hide-for-xlarge-up,th.hide-for-xlarge,td.hide-for-xlarge,th.show-for-xlarge-down,td.show-for-xlarge-down,th.hide-for-xxlarge-only,td.hide-for-xxlarge-only,th.hide-for-xxlarge-up,td.hide-for-xxlarge-up,th.hide-for-xxlarge,td.hide-for-xxlarge,th.show-for-xxlarge-down,td.show-for-xxlarge-down{display:table-cell !important}}@media only screen and (min-width: 64.0625em){.hide-for-small-only,.show-for-small-up,.hide-for-small,.hide-for-small-down,.hide-for-medium-only,.show-for-medium-up,.hide-for-medium,.hide-for-medium-down,.show-for-large-only,.show-for-large-up,.show-for-large,.show-for-large-down,.hide-for-xlarge-only,.hide-for-xlarge-up,.hide-for-xlarge,.show-for-xlarge-down,.hide-for-xxlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge,.show-for-xxlarge-down{display:inherit !important}.show-for-small-only,.hide-for-small-up,.show-for-small,.show-for-small-down,.show-for-medium-only,.hide-for-medium-up,.show-for-medium,.show-for-medium-down,.hide-for-large-only,.hide-for-large-up,.hide-for-large,.hide-for-large-down,.show-for-xlarge-only,.show-for-xlarge-up,.show-for-xlarge,.hide-for-xlarge-down,.show-for-xxlarge-only,.show-for-xxlarge-up,.show-for-xxlarge,.hide-for-xxlarge-down{display:none !important}.hidden-for-small-only,.visible-for-small-up,.hidden-for-small,.hidden-for-small-down,.hidden-for-medium-only,.visible-for-medium-up,.hidden-for-medium,.hidden-for-medium-down,.visible-for-large-only,.visible-for-large-up,.visible-for-large,.visible-for-large-down,.hidden-for-xlarge-only,.hidden-for-xlarge-up,.hidden-for-xlarge,.visible-for-xlarge-down,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up,.hidden-for-xxlarge,.visible-for-xxlarge-down{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.visible-for-small-only,.hidden-for-small-up,.visible-for-small,.visible-for-small-down,.visible-for-medium-only,.hidden-for-medium-up,.visible-for-medium,.visible-for-medium-down,.hidden-for-large-only,.hidden-for-large-up,.hidden-for-large,.hidden-for-large-down,.visible-for-xlarge-only,.visible-for-xlarge-up,.visible-for-xlarge,.hidden-for-xlarge-down,.visible-for-xxlarge-only,.visible-for-xxlarge-up,.visible-for-xxlarge,.hidden-for-xxlarge-down{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px}table.hide-for-small-only,table.show-for-small-up,table.hide-for-small,table.hide-for-small-down,table.hide-for-medium-only,table.show-for-medium-up,table.hide-for-medium,table.hide-for-medium-down,table.show-for-large-only,table.show-for-large-up,table.show-for-large,table.show-for-large-down,table.hide-for-xlarge-only,table.hide-for-xlarge-up,table.hide-for-xlarge,table.show-for-xlarge-down,table.hide-for-xxlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge,table.show-for-xxlarge-down{display:table !important}thead.hide-for-small-only,thead.show-for-small-up,thead.hide-for-small,thead.hide-for-small-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.hide-for-medium,thead.hide-for-medium-down,thead.show-for-large-only,thead.show-for-large-up,thead.show-for-large,thead.show-for-large-down,thead.hide-for-xlarge-only,thead.hide-for-xlarge-up,thead.hide-for-xlarge,thead.show-for-xlarge-down,thead.hide-for-xxlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge,thead.show-for-xxlarge-down{display:table-header-group !important}tbody.hide-for-small-only,tbody.show-for-small-up,tbody.hide-for-small,tbody.hide-for-small-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.show-for-large-only,tbody.show-for-large-up,tbody.show-for-large,tbody.show-for-large-down,tbody.hide-for-xlarge-only,tbody.hide-for-xlarge-up,tbody.hide-for-xlarge,tbody.show-for-xlarge-down,tbody.hide-for-xxlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge,tbody.show-for-xxlarge-down{display:table-row-group !important}tr.hide-for-small-only,tr.show-for-small-up,tr.hide-for-small,tr.hide-for-small-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.hide-for-medium,tr.hide-for-medium-down,tr.show-for-large-only,tr.show-for-large-up,tr.show-for-large,tr.show-for-large-down,tr.hide-for-xlarge-only,tr.hide-for-xlarge-up,tr.hide-for-xlarge,tr.show-for-xlarge-down,tr.hide-for-xxlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge,tr.show-for-xxlarge-down{display:table-row}th.hide-for-small-only,td.hide-for-small-only,th.show-for-small-up,td.show-for-small-up,th.hide-for-small,td.hide-for-small,th.hide-for-small-down,td.hide-for-small-down,th.hide-for-medium-only,td.hide-for-medium-only,th.show-for-medium-up,td.show-for-medium-up,th.hide-for-medium,td.hide-for-medium,th.hide-for-medium-down,td.hide-for-medium-down,th.show-for-large-only,td.show-for-large-only,th.show-for-large-up,td.show-for-large-up,th.show-for-large,td.show-for-large,th.show-for-large-down,td.show-for-large-down,th.hide-for-xlarge-only,td.hide-for-xlarge-only,th.hide-for-xlarge-up,td.hide-for-xlarge-up,th.hide-for-xlarge,td.hide-for-xlarge,th.show-for-xlarge-down,td.show-for-xlarge-down,th.hide-for-xxlarge-only,td.hide-for-xxlarge-only,th.hide-for-xxlarge-up,td.hide-for-xxlarge-up,th.hide-for-xxlarge,td.hide-for-xxlarge,th.show-for-xxlarge-down,td.show-for-xxlarge-down{display:table-cell !important}}@media only screen and (min-width: 90.0625em){.hide-for-small-only,.show-for-small-up,.hide-for-small,.hide-for-small-down,.hide-for-medium-only,.show-for-medium-up,.hide-for-medium,.hide-for-medium-down,.hide-for-large-only,.show-for-large-up,.hide-for-large,.hide-for-large-down,.show-for-xlarge-only,.show-for-xlarge-up,.show-for-xlarge,.show-for-xlarge-down,.hide-for-xxlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge,.show-for-xxlarge-down{display:inherit !important}.show-for-small-only,.hide-for-small-up,.show-for-small,.show-for-small-down,.show-for-medium-only,.hide-for-medium-up,.show-for-medium,.show-for-medium-down,.show-for-large-only,.hide-for-large-up,.show-for-large,.show-for-large-down,.hide-for-xlarge-only,.hide-for-xlarge-up,.hide-for-xlarge,.hide-for-xlarge-down,.show-for-xxlarge-only,.show-for-xxlarge-up,.show-for-xxlarge,.hide-for-xxlarge-down{display:none !important}.hidden-for-small-only,.visible-for-small-up,.hidden-for-small,.hidden-for-small-down,.hidden-for-medium-only,.visible-for-medium-up,.hidden-for-medium,.hidden-for-medium-down,.hidden-for-large-only,.visible-for-large-up,.hidden-for-large,.hidden-for-large-down,.visible-for-xlarge-only,.visible-for-xlarge-up,.visible-for-xlarge,.visible-for-xlarge-down,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up,.hidden-for-xxlarge,.visible-for-xxlarge-down{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.visible-for-small-only,.hidden-for-small-up,.visible-for-small,.visible-for-small-down,.visible-for-medium-only,.hidden-for-medium-up,.visible-for-medium,.visible-for-medium-down,.visible-for-large-only,.hidden-for-large-up,.visible-for-large,.visible-for-large-down,.hidden-for-xlarge-only,.hidden-for-xlarge-up,.hidden-for-xlarge,.hidden-for-xlarge-down,.visible-for-xxlarge-only,.visible-for-xxlarge-up,.visible-for-xxlarge,.hidden-for-xxlarge-down{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px}table.hide-for-small-only,table.show-for-small-up,table.hide-for-small,table.hide-for-small-down,table.hide-for-medium-only,table.show-for-medium-up,table.hide-for-medium,table.hide-for-medium-down,table.hide-for-large-only,table.show-for-large-up,table.hide-for-large,table.hide-for-large-down,table.show-for-xlarge-only,table.show-for-xlarge-up,table.show-for-xlarge,table.show-for-xlarge-down,table.hide-for-xxlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge,table.show-for-xxlarge-down{display:table !important}thead.hide-for-small-only,thead.show-for-small-up,thead.hide-for-small,thead.hide-for-small-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.hide-for-medium,thead.hide-for-medium-down,thead.hide-for-large-only,thead.show-for-large-up,thead.hide-for-large,thead.hide-for-large-down,thead.show-for-xlarge-only,thead.show-for-xlarge-up,thead.show-for-xlarge,thead.show-for-xlarge-down,thead.hide-for-xxlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge,thead.show-for-xxlarge-down{display:table-header-group !important}tbody.hide-for-small-only,tbody.show-for-small-up,tbody.hide-for-small,tbody.hide-for-small-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.hide-for-large-only,tbody.show-for-large-up,tbody.hide-for-large,tbody.hide-for-large-down,tbody.show-for-xlarge-only,tbody.show-for-xlarge-up,tbody.show-for-xlarge,tbody.show-for-xlarge-down,tbody.hide-for-xxlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge,tbody.show-for-xxlarge-down{display:table-row-group !important}tr.hide-for-small-only,tr.show-for-small-up,tr.hide-for-small,tr.hide-for-small-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.hide-for-medium,tr.hide-for-medium-down,tr.hide-for-large-only,tr.show-for-large-up,tr.hide-for-large,tr.hide-for-large-down,tr.show-for-xlarge-only,tr.show-for-xlarge-up,tr.show-for-xlarge,tr.show-for-xlarge-down,tr.hide-for-xxlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge,tr.show-for-xxlarge-down{display:table-row}th.hide-for-small-only,td.hide-for-small-only,th.show-for-small-up,td.show-for-small-up,th.hide-for-small,td.hide-for-small,th.hide-for-small-down,td.hide-for-small-down,th.hide-for-medium-only,td.hide-for-medium-only,th.show-for-medium-up,td.show-for-medium-up,th.hide-for-medium,td.hide-for-medium,th.hide-for-medium-down,td.hide-for-medium-down,th.hide-for-large-only,td.hide-for-large-only,th.show-for-large-up,td.show-for-large-up,th.hide-for-large,td.hide-for-large,th.hide-for-large-down,td.hide-for-large-down,th.show-for-xlarge-only,td.show-for-xlarge-only,th.show-for-xlarge-up,td.show-for-xlarge-up,th.show-for-xlarge,td.show-for-xlarge,th.show-for-xlarge-down,td.show-for-xlarge-down,th.hide-for-xxlarge-only,td.hide-for-xxlarge-only,th.hide-for-xxlarge-up,td.hide-for-xxlarge-up,th.hide-for-xxlarge,td.hide-for-xxlarge,th.show-for-xxlarge-down,td.show-for-xxlarge-down{display:table-cell !important}}@media only screen and (min-width: 120.0625em){.hide-for-small-only,.show-for-small-up,.hide-for-small,.hide-for-small-down,.hide-for-medium-only,.show-for-medium-up,.hide-for-medium,.hide-for-medium-down,.hide-for-large-only,.show-for-large-up,.hide-for-large,.hide-for-large-down,.hide-for-xlarge-only,.show-for-xlarge-up,.hide-for-xlarge,.hide-for-xlarge-down,.show-for-xxlarge-only,.show-for-xxlarge-up,.show-for-xxlarge,.show-for-xxlarge-down{display:inherit !important}.show-for-small-only,.hide-for-small-up,.show-for-small,.show-for-small-down,.show-for-medium-only,.hide-for-medium-up,.show-for-medium,.show-for-medium-down,.show-for-large-only,.hide-for-large-up,.show-for-large,.show-for-large-down,.show-for-xlarge-only,.hide-for-xlarge-up,.show-for-xlarge,.show-for-xlarge-down,.hide-for-xxlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge,.hide-for-xxlarge-down{display:none !important}.hidden-for-small-only,.visible-for-small-up,.hidden-for-small,.hidden-for-small-down,.hidden-for-medium-only,.visible-for-medium-up,.hidden-for-medium,.hidden-for-medium-down,.hidden-for-large-only,.visible-for-large-up,.hidden-for-large,.hidden-for-large-down,.hidden-for-xlarge-only,.visible-for-xlarge-up,.hidden-for-xlarge,.hidden-for-xlarge-down,.visible-for-xxlarge-only,.visible-for-xxlarge-up,.visible-for-xxlarge,.visible-for-xxlarge-down{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.visible-for-small-only,.hidden-for-small-up,.visible-for-small,.visible-for-small-down,.visible-for-medium-only,.hidden-for-medium-up,.visible-for-medium,.visible-for-medium-down,.visible-for-large-only,.hidden-for-large-up,.visible-for-large,.visible-for-large-down,.visible-for-xlarge-only,.hidden-for-xlarge-up,.visible-for-xlarge,.visible-for-xlarge-down,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up,.hidden-for-xxlarge,.hidden-for-xxlarge-down{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px}table.hide-for-small-only,table.show-for-small-up,table.hide-for-small,table.hide-for-small-down,table.hide-for-medium-only,table.show-for-medium-up,table.hide-for-medium,table.hide-for-medium-down,table.hide-for-large-only,table.show-for-large-up,table.hide-for-large,table.hide-for-large-down,table.hide-for-xlarge-only,table.show-for-xlarge-up,table.hide-for-xlarge,table.hide-for-xlarge-down,table.show-for-xxlarge-only,table.show-for-xxlarge-up,table.show-for-xxlarge,table.show-for-xxlarge-down{display:table !important}thead.hide-for-small-only,thead.show-for-small-up,thead.hide-for-small,thead.hide-for-small-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.hide-for-medium,thead.hide-for-medium-down,thead.hide-for-large-only,thead.show-for-large-up,thead.hide-for-large,thead.hide-for-large-down,thead.hide-for-xlarge-only,thead.show-for-xlarge-up,thead.hide-for-xlarge,thead.hide-for-xlarge-down,thead.show-for-xxlarge-only,thead.show-for-xxlarge-up,thead.show-for-xxlarge,thead.show-for-xxlarge-down{display:table-header-group !important}tbody.hide-for-small-only,tbody.show-for-small-up,tbody.hide-for-small,tbody.hide-for-small-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.hide-for-large-only,tbody.show-for-large-up,tbody.hide-for-large,tbody.hide-for-large-down,tbody.hide-for-xlarge-only,tbody.show-for-xlarge-up,tbody.hide-for-xlarge,tbody.hide-for-xlarge-down,tbody.show-for-xxlarge-only,tbody.show-for-xxlarge-up,tbody.show-for-xxlarge,tbody.show-for-xxlarge-down{display:table-row-group !important}tr.hide-for-small-only,tr.show-for-small-up,tr.hide-for-small,tr.hide-for-small-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.hide-for-medium,tr.hide-for-medium-down,tr.hide-for-large-only,tr.show-for-large-up,tr.hide-for-large,tr.hide-for-large-down,tr.hide-for-xlarge-only,tr.show-for-xlarge-up,tr.hide-for-xlarge,tr.hide-for-xlarge-down,tr.show-for-xxlarge-only,tr.show-for-xxlarge-up,tr.show-for-xxlarge,tr.show-for-xxlarge-down{display:table-row}th.hide-for-small-only,td.hide-for-small-only,th.show-for-small-up,td.show-for-small-up,th.hide-for-small,td.hide-for-small,th.hide-for-small-down,td.hide-for-small-down,th.hide-for-medium-only,td.hide-for-medium-only,th.show-for-medium-up,td.show-for-medium-up,th.hide-for-medium,td.hide-for-medium,th.hide-for-medium-down,td.hide-for-medium-down,th.hide-for-large-only,td.hide-for-large-only,th.show-for-large-up,td.show-for-large-up,th.hide-for-large,td.hide-for-large,th.hide-for-large-down,td.hide-for-large-down,th.hide-for-xlarge-only,td.hide-for-xlarge-only,th.show-for-xlarge-up,td.show-for-xlarge-up,th.hide-for-xlarge,td.hide-for-xlarge,th.hide-for-xlarge-down,td.hide-for-xlarge-down,th.show-for-xxlarge-only,td.show-for-xxlarge-only,th.show-for-xxlarge-up,td.show-for-xxlarge-up,th.show-for-xxlarge,td.show-for-xxlarge,th.show-for-xxlarge-down,td.show-for-xxlarge-down{display:table-cell !important}}.show-for-landscape,.hide-for-portrait{display:inherit !important}.hide-for-landscape,.show-for-portrait{display:none !important}table.hide-for-landscape,table.show-for-portrait{display:table !important}thead.hide-for-landscape,thead.show-for-portrait{display:table-header-group !important}tbody.hide-for-landscape,tbody.show-for-portrait{display:table-row-group !important}tr.hide-for-landscape,tr.show-for-portrait{display:table-row !important}td.hide-for-landscape,td.show-for-portrait,th.hide-for-landscape,th.show-for-portrait{display:table-cell !important}@media only screen and (orientation: landscape){.show-for-landscape,.hide-for-portrait{display:inherit !important}.hide-for-landscape,.show-for-portrait{display:none !important}table.show-for-landscape,table.hide-for-portrait{display:table !important}thead.show-for-landscape,thead.hide-for-portrait{display:table-header-group !important}tbody.show-for-landscape,tbody.hide-for-portrait{display:table-row-group !important}tr.show-for-landscape,tr.hide-for-portrait{display:table-row !important}td.show-for-landscape,td.hide-for-portrait,th.show-for-landscape,th.hide-for-portrait{display:table-cell !important}}@media only screen and (orientation: portrait){.show-for-portrait,.hide-for-landscape{display:inherit !important}.hide-for-portrait,.show-for-landscape{display:none !important}table.show-for-portrait,table.hide-for-landscape{display:table !important}thead.show-for-portrait,thead.hide-for-landscape{display:table-header-group !important}tbody.show-for-portrait,tbody.hide-for-landscape{display:table-row-group !important}tr.show-for-portrait,tr.hide-for-landscape{display:table-row !important}td.show-for-portrait,td.hide-for-landscape,th.show-for-portrait,th.hide-for-landscape{display:table-cell !important}}.show-for-touch{display:none !important}.hide-for-touch{display:inherit !important}.touch .show-for-touch{display:inherit !important}.touch .hide-for-touch{display:none !important}table.hide-for-touch{display:table !important}.touch table.show-for-touch{display:table !important}thead.hide-for-touch{display:table-header-group !important}.touch thead.show-for-touch{display:table-header-group !important}tbody.hide-for-touch{display:table-row-group !important}.touch tbody.show-for-touch{display:table-row-group !important}tr.hide-for-touch{display:table-row !important}.touch tr.show-for-touch{display:table-row !important}td.hide-for-touch{display:table-cell !important}.touch td.show-for-touch{display:table-cell !important}th.hide-for-touch{display:table-cell !important}.touch th.show-for-touch{display:table-cell !important}.show-for-sr{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px}.show-on-focus{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px}.show-on-focus:focus,.show-on-focus:active{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.print-only{display:none !important}@media print{*{background:transparent !important;box-shadow:none !important;color:#000 !important;text-shadow:none !important}.show-for-print{display:block}.hide-for-print{display:none}table.show-for-print{display:table !important}thead.show-for-print{display:table-header-group !important}tbody.show-for-print{display:table-row-group !important}tr.show-for-print{display:table-row !important}td.show-for-print{display:table-cell !important}th.show-for-print{display:table-cell !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.hide-on-print{display:none !important}.print-only{display:block !important}.hide-for-print{display:none !important}.show-for-print{display:inherit !important}}@media print{.show-for-print{display:block}.hide-for-print{display:none}table.show-for-print{display:table !important}thead.show-for-print{display:table-header-group !important}tbody.show-for-print{display:table-row-group !important}tr.show-for-print{display:table-row !important}td.show-for-print{display:table-cell !important}th.show-for-print{display:table-cell !important}}@media not print{.show-for-print{display:none !important}} \ No newline at end of file diff --git a/cake-3.1/webroot/css/cake.css b/cake-3.1/webroot/css/cake.css new file mode 100644 index 000000000..29ef9f114 --- /dev/null +++ b/cake-3.1/webroot/css/cake.css @@ -0,0 +1,565 @@ +.disabled a, +a.disabled { + pointer-events: none; +} + +a:hover { + color: #15848F; +} + +a { + color: #1798A5; +} + +.side-nav li a:not(.button) { + color: #15848F; +} + +.side-nav li a:not(.button):hover { + color: #15848F; +} + +header { + background-color: #15848F; + color: #ffffff; + font-size: 30px; + height: 84px; + line-height: 64px; + padding: 16px 0px; + box-shadow: 0px 1px rgba(0, 0, 0, 0.24); +} + +header .header-title { + padding-left:80px +} + + +legend { + color:#15848F; +} + +.row { + max-width: 80rem; +} + +.actions.columns { + margin-top:1rem; + border-left: 5px solid #15848F; + padding-left: 15px; + padding: 32px 20px; +} + +.actions.columns h3 { + color:#15848F; +} + +.index table { + margin-top: 2rem; + border: 0; + width: 100%; + table-layout: fixed; +} + +.related table { + border: 0; + width: 100%; + table-layout: fixed; +} + +.index table thead { + height: 3.5rem; +} + +.header-help { + float: right; + margin-right:2rem; + margin-top: -80px; + font-size:16px; +} + +.header-help span { + font-weight: normal; + text-align: center; + text-decoration: none; + line-height: 1; + white-space: nowrap; + display: inline-block; + padding: 0.25rem 0.5rem 0.375rem; + font-size: 0.8rem; + background-color: #0097a7; + color: #FFF; + border-radius: 1000px; +} + +.header-help a { + color: #fff; +} + +ul.pagination li a { + color: rgba(0, 0 ,0 , 0.54); +} + +ul.pagination li.active a { + background-color: #DCE47E; + color: #FFF; + font-weight: bold; + cursor: default; +} +ul.pagination .disabled:hover a { + background: none; +} + +.paginator { + text-align: center; +} + +.paginator ul.pagination li { + float: none; + display: inline-block; +} + +.paginator p { + text-align: right; + color: rgba(0, 0 ,0 , 0.54); +} + +.asc:after { + content: " \2193"; +} +.desc:after { + content: " \2191"; +} + +button { + background: #8D6E65; +} + +.form button:hover, .form button:focus { + background: #7A6058; + box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.26) !important; +} + +.form button[type="submit"] { + float: right; + text-transform: uppercase; + box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.26); +} + +.form .error-message { + display: block; + padding: 0.375rem 0.5625rem 0.5625rem; + margin-top: -1px; + margin-bottom: 1rem; + font-size: 0.75rem; + font-weight: normal; + font-style: italic; + color: rgba(0, 0, 0, 0.54); +} + +.required > label { + font-weight: bold; +} +.required > label:after { + content: ' *'; + color: #C3232D; +} + +select[multiple] { + min-height:150px; + background: none; +} +input[type=checkbox], +input[type=radio] { + margin-right: 0.5em; +} + +.date select, +.time select, +.datetime select { + display: inline; + width: auto; + margin-right: 10px; +} + +.error label, +.error label.error { + color: #C3232D; +} + +.view h2 { + color: #6F6F6F; +} + +.view .columns.strings { + border-radius: 3px; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.24); + margin-right:0.7rem; +} + +.view .numbers { + background-color: #B7E3EC; + color: #FFF; + border-radius: 3px; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.24); + margin-right: 0.7rem; +} + +.view .columns.dates { + border-radius: 3px; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.24); + margin-right:0.7rem; + background-color:#DCE47E; + color: #fff; +} + +.view .columns.booleans { + border-radius: 3px; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.24); + margin-right:0.7rem; + background-color: #8D6E65; + color: #fff; +} + +.view .strings p { + border-bottom: 1px solid #eee; +} +.view .numbers .subheader, .view .dates .subheader { + color:#747474; +} +.view .booleans .subheader { + color: #E9E9E9 +} + +.view .texts .columns { + margin-top:1.2rem; + border-bottom: 1px solid #eee; +} + + +/** Notices and Errors **/ +.cake-error, +.cake-debug, +.notice, +p.error, +p.notice { + display: block; + clear: both; + background-repeat: repeat-x; + margin-bottom: 18px; + padding: 7px 14px; + border-radius: 3px; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.24); +} + +.cake-debug, +.notice, +p.notice { + color: #000000; + background: #ffcc00; +} + +.cake-error, +p.error { + color: #fff; + background: #C3232D; +} + +pre { + background: none repeat scroll 0% 0% #FFF; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.24); + margin: 15px 0px; + color: rgba(0, 0 ,0 , 0.74); + padding:5px; +} + +.cake-error .cake-stack-trace { + margin-top:10px; +} + +.cake-stack-trace code { + background: inherit; + border:0; +} + +.cake-code-dump .code-highlight { + display: block; + background-color: #FFC600; +} + +.cake-error a, +.cake-error a:hover { + color:#fff; + text-decoration: underline; +} + +.home header { + width: 100%; + height: 85%; + position: relative; + display: table; +} + +.home h1 { + font-family: "Gill Sans MT", Calibri, sans-serif; +} + +.home header .header-image { + display: table-cell; + vertical-align: middle; + text-align: center; +} + +.home header h1 { + color: #fff; +} + +.home .checks { + padding:30px; + color: #626262; + border-radius: 3px; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.24); + margin-top:50px; +} + +.checks.url-rewriting { + background-color: #F0F0F0; + display: none; +} + +.checks.platform { + background-color: #B7E3EC; +} + +.checks.filesystem { + background: #DCE47E; +} + +.checks.database { + background-color: #DFF0D8; + padding-bottom: 10px; + margin-bottom: 30px; +} + +.home .checks .success:before, .home .checks .problem:before { + line-height: 0px; + font-size: 28px; + height: 12px; + width: 12px; + border-radius: 15px; + text-align: center; + vertical-align: middle; + display: inline-block; + position: relative; + left: -11px; +} + +.home .checks .success:before { + content: "✓"; + color: green; + margin-right: 9px; +} + +.home .checks .problem:before { + content: "✘"; + color: red; + margin-right: 9px; +} + +.top-bar.expanded .title-area { + background: #01545b; +} + +.top-bar.expanded, .top-bar,.top-bar-section ul li,.top-bar-section li:not(.has-form) a:not(.button) { + background: #116d76; +} + +.top-bar-section li:not(.has-form) a:not(.button):hover { + background-color: #308e97; + background: #308e97; +} + +.side-nav li.heading { + color: #1798A5; + font-size: 0.875rem; + font-weight: bold; + text-transform: uppercase; + padding: 0.4375rem 0.875rem; +} + +#actions-sidebar { + background: #fafafa; +} + +.index table { + margin-top: 0rem; + border: 0; +} + +table { + background: #fff; + margin-bottom: 1.25rem; + border: none; + table-layout: fixed; + width: 100%; +} + +table thead { + background: none; +} + +table tr { + border-bottom: 1px solid #ebebec; +} + +table thead tr { + border-bottom: 1px solid #1798A5; +} + +table tr th { + padding: 0.5625rem 0.625rem; + font-size: 0.875rem; + color: #1798A5; + text-align: left; + border-bottom: 2px solid #1798A5; +} + +table tr:nth-of-type(even) { + background: none; +} + +fieldset { + border: none; + padding: 1.25rem; + margin: 1.125rem 0; +} + +fieldset legend { + border-bottom: 2px solid #1798A5; + width: 100%; + line-height: 2rem; +} + +.form button[type="submit"] { + float: right; + text-transform: uppercase; + box-shadow: none; +} + +.form button:hover, .form button:focus { + background: #BE840B; + box-shadow: none; +} + +button { + background: #966600; +} + +div.message { + cursor: pointer; + display: block; + font-weight: normal; + padding: 0 1.5rem 0 1.5rem; + transition: height 300ms ease-out 0s; + background-color: #a0d3e8; + color: #626262; + position: fixed; + top: 15px; + right: 15px; + z-index: 999; + overflow: hidden; + height: 50px; + line-height: 2.5em; +} + +div.message:before { + line-height: 0px; + font-size: 20px; + height: 12px; + width: 12px; + border-radius: 15px; + text-align: center; + vertical-align: middle; + display: inline-block; + position: relative; + left: -11px; + background-color: #FFF; + padding: 12px 14px 12px 10px; + content: "i"; + color: #a0d3e8; +} + +div.message.error { + background-color: #C3232D; + color: #FFF; +} + +div.message.error:before { + padding: 11px 16px 14px 7px; + color: #C3232D; + content: "x"; +} +div.message.hidden { + height: 0; +} + + +.vertical-table th { + padding: 0.5625rem 0.625rem; + font-size: 0.875rem; + color: #1798A5; + border: none; + text-align: left; +} + +.vertical-table { + vertical-align: middle; +} + +.vertical-table td { + text-align: right; +} + + +.content { + padding: 2rem; +} + +/* Use 'one true layout' methods to get equal height columns */ +.container { + overflow: hidden; + min-height: 92%; /* full height almost always */ +} + +/* Force equal height by overflowing */ +.content, +#actions-sidebar { + margin-bottom: -99999px; + padding-bottom: 99999px; +} +@media(max-width: 640px) { + #actions-sidebar { + padding-bottom: 2rem; + margin-bottom: 0; + } +} + +.content h3 { + color: #be140b; + padding-bottom: 0.5rem; + margin-bottom: 20px; +} + +.content h4 { + color: #be140b; + padding-bottom: 0.5rem; + margin-bottom: 20px; + border-bottom: 2px solid #be140b; +} + +.content .related h4 { + color: #ebaa20; + padding-bottom: 0.5rem; + margin-top: 20px; + margin-bottom: 10px; + border-bottom: 0px; +} diff --git a/cake-3.1/webroot/favicon.ico b/cake-3.1/webroot/favicon.ico new file mode 100644 index 000000000..aaac9c853 Binary files /dev/null and b/cake-3.1/webroot/favicon.ico differ diff --git a/cake-3.1/webroot/img/cake.icon.png b/cake-3.1/webroot/img/cake.icon.png new file mode 100644 index 000000000..394fa42d5 Binary files /dev/null and b/cake-3.1/webroot/img/cake.icon.png differ diff --git a/cake-3.1/webroot/img/cake.power.gif b/cake-3.1/webroot/img/cake.power.gif new file mode 100644 index 000000000..8f8d570a2 Binary files /dev/null and b/cake-3.1/webroot/img/cake.power.gif differ diff --git a/cake-3.1/webroot/index.php b/cake-3.1/webroot/index.php new file mode 100644 index 000000000..19cef45a8 --- /dev/null +++ b/cake-3.1/webroot/index.php @@ -0,0 +1,39 @@ +dispatch( + Request::createFromGlobals(), + new Response() +); + +require $_SERVER['DOCUMENT_ROOT'].'/php-framework-benchmark/libs/output_data.php'; diff --git a/fuel-1.8-dev/public/assets/css/index.html b/cake-3.1/webroot/js/empty similarity index 100% rename from fuel-1.8-dev/public/assets/css/index.html rename to cake-3.1/webroot/js/empty diff --git a/cake-3.2/.editorconfig b/cake-3.2/.editorconfig new file mode 100644 index 000000000..706190175 --- /dev/null +++ b/cake-3.2/.editorconfig @@ -0,0 +1,18 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.bat] +end_of_line = crlf + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/cake-3.2/.gitattributes b/cake-3.2/.gitattributes new file mode 100644 index 000000000..926a808fc --- /dev/null +++ b/cake-3.2/.gitattributes @@ -0,0 +1,36 @@ +# Define the line ending behavior of the different file extensions +# Set default behaviour, in case users don't have core.autocrlf set. +* text=auto +* text eol=lf + +# Explicitly declare text files we want to always be normalized and converted +# to native line endings on checkout. +*.php text +*.default text +*.ctp text +*.sql text +*.md text +*.po text +*.js text +*.css text +*.ini text +*.properties text +*.txt text +*.xml text +*.yml text +.htaccess text + +# Declare files that will always have CRLF line endings on checkout. +*.bat eol=crlf + +# Declare files that will always have LF line endings on checkout. +*.pem eol=lf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.gif binary +*.ico binary +*.mo binary +*.pdf binary +*.phar binary diff --git a/cake-3.2/.gitignore b/cake-3.2/.gitignore new file mode 100644 index 000000000..e2e825dcc --- /dev/null +++ b/cake-3.2/.gitignore @@ -0,0 +1,4 @@ +/vendor/* +/config/app.php +/tmp/* +/logs/* diff --git a/cake-3.2/.travis.yml b/cake-3.2/.travis.yml new file mode 100644 index 000000000..d10f74062 --- /dev/null +++ b/cake-3.2/.travis.yml @@ -0,0 +1,16 @@ +language: php + +sudo: false + +php: + - 7.0 + +before_script: + - sh -c "composer require 'cakephp/cakephp-codesniffer:dev-master'" + - phpenv rehash + +script: + - sh -c "vendor/bin/phpcs -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests ./config ./webroot" + +notifications: + email: false diff --git a/cake-3.2/README.md b/cake-3.2/README.md new file mode 100644 index 000000000..5568240be --- /dev/null +++ b/cake-3.2/README.md @@ -0,0 +1,26 @@ +# CakePHP Application Skeleton + +[![Build Status](https://img.shields.io/travis/cakephp/app/master.svg?style=flat-square)](https://travis-ci.org/cakephp/app) +[![License](https://img.shields.io/packagist/l/cakephp/app.svg?style=flat-square)](https://packagist.org/packages/cakephp/app) + +A skeleton for creating applications with [CakePHP](http://cakephp.org) 3.x. + +The framework source code can be found here: [cakephp/cakephp](https://github.com/cakephp/cakephp). + +## Installation + +1. Download [Composer](http://getcomposer.org/doc/00-intro.md) or update `composer self-update`. +2. Run `php composer.phar create-project --prefer-dist cakephp/app [app_name]`. + +If Composer is installed globally, run +```bash +composer create-project --prefer-dist cakephp/app [app_name] +``` + +You should now be able to visit the path to where you installed the app and see +the setup traffic lights. + +## Configuration + +Read and edit `config/app.php` and setup the 'Datasources' and any other +configuration relevant for your application. diff --git a/cake-3.2/_benchmark/hello_world.sh b/cake-3.2/_benchmark/hello_world.sh new file mode 100644 index 000000000..f0f049590 --- /dev/null +++ b/cake-3.2/_benchmark/hello_world.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +url="$base/$fw/index.php/hello/index" diff --git a/cake-3.2/_benchmark/setup.sh b/cake-3.2/_benchmark/setup.sh new file mode 100644 index 000000000..566311de0 --- /dev/null +++ b/cake-3.2/_benchmark/setup.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +sudo rm -rf tmp/* +composer install --no-interaction --no-dev --optimize-autoloader diff --git a/cake-3.2/bin/cake b/cake-3.2/bin/cake new file mode 100755 index 000000000..6801c45b1 --- /dev/null +++ b/cake-3.2/bin/cake @@ -0,0 +1,46 @@ +#!/usr/bin/env sh +################################################################################ +# +# Cake is a shell script for invoking CakePHP shell commands +# +# CakePHP(tm) : Rapid Development Framework (http://cakephp.org) +# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +# +# Licensed under The MIT License +# For full copyright and license information, please see the LICENSE.txt +# Redistributions of files must retain the above copyright notice. +# +# @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +# @link http://cakephp.org CakePHP(tm) Project +# @since 1.2.0 +# @license http://www.opensource.org/licenses/mit-license.php MIT License +# +################################################################################ + +# Canonicalize by following every symlink of the given name recursively +canonicalize() { + NAME="$1" + if [ -f "$NAME" ] + then + DIR=$(dirname -- "$NAME") + NAME=$(cd -P "$DIR" > /dev/null && pwd -P)/$(basename -- "$NAME") + fi + while [ -h "$NAME" ]; do + DIR=$(dirname -- "$NAME") + SYM=$(readlink "$NAME") + NAME=$(cd "$DIR" > /dev/null && cd $(dirname -- "$SYM") > /dev/null && pwd)/$(basename -- "$SYM") + done + echo "$NAME" +} + +CONSOLE=$(dirname -- "$(canonicalize "$0")") +APP=$(dirname "$CONSOLE") + +if [ $(basename $0) != 'cake' ] +then + exec php "$CONSOLE"/cake.php $(basename $0) "$@" +else + exec php "$CONSOLE"/cake.php "$@" +fi + +exit diff --git a/cake-3.2/bin/cake.bat b/cake-3.2/bin/cake.bat new file mode 100644 index 000000000..d4512dbf6 --- /dev/null +++ b/cake-3.2/bin/cake.bat @@ -0,0 +1,27 @@ +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:: +:: Cake is a Windows batch script for invoking CakePHP shell commands +:: +:: CakePHP(tm) : Rapid Development Framework (http://cakephp.org) +:: Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +:: +:: Licensed under The MIT License +:: Redistributions of files must retain the above copyright notice. +:: +:: @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +:: @link http://cakephp.org CakePHP(tm) Project +:: @since 2.0.0 +:: @license http://www.opensource.org/licenses/mit-license.php MIT License +:: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +@echo off + +SET app=%0 +SET lib=%~dp0 + +php "%lib%cake.php" %* + +echo. + +exit /B %ERRORLEVEL% diff --git a/cake-3.2/bin/cake.php b/cake-3.2/bin/cake.php new file mode 100644 index 000000000..6e4a16aed --- /dev/null +++ b/cake-3.2/bin/cake.php @@ -0,0 +1,33 @@ +#!/usr/bin/php -q +require->php)) { + $minVersion = preg_replace('/([^0-9\.])/', '', $composer->require->php); + } +} +if (version_compare(phpversion(), $minVersion, '<')) { + fwrite(STDERR, sprintf("Minimum PHP version: %s. You are using: %s.\n", $minVersion, phpversion())); + exit(-1); +} + +include dirname(__DIR__) . '/config/bootstrap.php'; + +exit(Cake\Console\ShellDispatcher::run($argv)); diff --git a/cake-3.2/composer.json b/cake-3.2/composer.json new file mode 100644 index 000000000..22774cd6c --- /dev/null +++ b/cake-3.2/composer.json @@ -0,0 +1,41 @@ +{ + "name": "cakephp/app", + "description": "CakePHP skeleton app", + "homepage": "http://cakephp.org", + "type": "project", + "license": "MIT", + "require": { + "php": ">=5.5.9", + "cakephp/cakephp": "3.2.*", + "mobiledetect/mobiledetectlib": "2.*", + "cakephp/migrations": "~1.0", + "cakephp/plugin-installer": "*" + }, + "require-dev": { + "psy/psysh": "@stable", + "cakephp/debug_kit": "~3.2", + "cakephp/bake": "~1.1" + }, + "suggest": { + "phpunit/phpunit": "Allows automated tests to be run without system-wide install.", + "cakephp/cakephp-codesniffer": "Allows to check the code against the coding standards used in CakePHP." + }, + "autoload": { + "psr-4": { + "App\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "App\\Test\\": "tests", + "Cake\\Test\\": "./vendor/cakephp/cakephp/tests" + } + }, + "scripts": { + "post-install-cmd": "App\\Console\\Installer::postInstall", + "post-create-project-cmd": "App\\Console\\Installer::postInstall", + "post-autoload-dump": "Cake\\Composer\\Installer\\PluginInstaller::postAutoloadDump" + }, + "minimum-stability": "stable", + "prefer-stable": true +} diff --git a/cake-3.2/composer.lock b/cake-3.2/composer.lock new file mode 100644 index 000000000..589a49ec1 --- /dev/null +++ b/cake-3.2/composer.lock @@ -0,0 +1,1879 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "3e7f096da3058c419b5a6e4314cd3cce", + "content-hash": "702105ea937f40b3337a487b34a7d009", + "packages": [ + { + "name": "aura/installer-default", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/auraphp/installer-default.git", + "reference": "52f8de3670cc1ef45a916f40f732937436d028c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/auraphp/installer-default/zipball/52f8de3670cc1ef45a916f40f732937436d028c8", + "reference": "52f8de3670cc1ef45a916f40f732937436d028c8", + "shasum": "" + }, + "type": "composer-installer", + "extra": { + "class": "Aura\\Composer\\DefaultInstaller" + }, + "autoload": { + "psr-0": { + "Aura\\Composer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Paul M. Jones", + "email": "pmjones88@gmail.com", + "homepage": "http://paul-m-jones.com" + } + ], + "description": "Installs Aura packages using the Composer defaults.", + "keywords": [ + "aura", + "installer" + ], + "time": "2012-11-26 21:35:57" + }, + { + "name": "aura/intl", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/auraphp/Aura.Intl.git", + "reference": "c5fe620167550ad6fa77dd3570fba2efc77a2a21" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/auraphp/Aura.Intl/zipball/c5fe620167550ad6fa77dd3570fba2efc77a2a21", + "reference": "c5fe620167550ad6fa77dd3570fba2efc77a2a21", + "shasum": "" + }, + "require": { + "aura/installer-default": "1.0.*", + "php": ">=5.4.0" + }, + "type": "aura-package", + "extra": { + "aura": { + "type": "library", + "config": { + "common": "Aura\\Intl\\_Config\\Common" + } + }, + "branch-alias": { + "dev-develop": "1.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Aura\\Intl": "src/" + }, + "psr-4": { + "Aura\\Intl\\_Config\\": "config/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Paul M. Jones", + "email": "pmjones88@gmail.com", + "homepage": "http://paul-m-jones.com" + }, + { + "name": "Aura.Intl Contributors", + "homepage": "https://github.com/auraphp/Aura.Intl/contributors" + }, + { + "name": "Pascal Borreli", + "email": "pascal@borreli.com" + }, + { + "name": "Mapthegod", + "email": "mapthegod@gmail.com" + }, + { + "name": "Jose Lorenzo Rodriguez", + "email": "jose.zap@gmail.com" + } + ], + "description": "The Aura.Intl package provides internationalization (I18N) tools, specifically\npackage-oriented per-locale message translation.", + "homepage": "http://auraphp.com/Aura.Intl", + "keywords": [ + "g11n", + "globalization", + "i18n", + "internationalization", + "intl", + "l10n", + "localization" + ], + "time": "2014-08-24 00:00:00" + }, + { + "name": "cakephp/cakephp", + "version": "3.2.14", + "source": { + "type": "git", + "url": "https://github.com/cakephp/cakephp.git", + "reference": "54fa6f3a9ae6f8dea120c2a7dd76406b35879f3a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/cakephp/zipball/54fa6f3a9ae6f8dea120c2a7dd76406b35879f3a", + "reference": "54fa6f3a9ae6f8dea120c2a7dd76406b35879f3a", + "shasum": "" + }, + "require": { + "aura/intl": "1.1.*", + "cakephp/chronos": "~1.0", + "ext-intl": "*", + "ext-mbstring": "*", + "php": ">=5.5.9", + "psr/log": "1.0" + }, + "replace": { + "cakephp/cache": "self.version", + "cakephp/collection": "self.version", + "cakephp/core": "self.version", + "cakephp/database": "self.version", + "cakephp/datasource": "self.version", + "cakephp/event": "self.version", + "cakephp/filesystem": "self.version", + "cakephp/i18n": "self.version", + "cakephp/log": "self.version", + "cakephp/orm": "self.version", + "cakephp/utility": "self.version", + "cakephp/validation": "self.version" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "~2.1", + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-openssl": "To use Security::encrypt() or have secure CSRF token generation." + }, + "type": "library", + "autoload": { + "psr-4": { + "Cake\\": "src" + }, + "files": [ + "src/Core/functions.php", + "src/Collection/functions.php", + "src/I18n/functions.php", + "src/Utility/bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/cakephp/graphs/contributors" + } + ], + "description": "The CakePHP framework", + "homepage": "http://cakephp.org", + "keywords": [ + "framework" + ], + "time": "2016-08-13 01:58:06" + }, + { + "name": "cakephp/chronos", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/cakephp/chronos.git", + "reference": "16753ad209f5b478dd35caadcd609104762fc157" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/chronos/zipball/16753ad209f5b478dd35caadcd609104762fc157", + "reference": "16753ad209f5b478dd35caadcd609104762fc157", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "athletic/athletic": "~0.1", + "cakephp/cakephp-codesniffer": "dev-master", + "phpunit/phpunit": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cake\\Chronos\\": "src" + }, + "files": [ + "src/carbon_compat.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "http://nesbot.com" + }, + { + "name": "The CakePHP Team", + "homepage": "http://cakephp.org" + } + ], + "description": "A simple API extension for DateTime.", + "homepage": "http://cakephp.org", + "keywords": [ + "date", + "datetime", + "time" + ], + "time": "2017-01-09 02:11:45" + }, + { + "name": "cakephp/migrations", + "version": "1.6.7", + "source": { + "type": "git", + "url": "https://github.com/cakephp/migrations.git", + "reference": "f2a5e2915f8924bd36979a1001edeef7cd455f89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/migrations/zipball/f2a5e2915f8924bd36979a1001edeef7cd455f89", + "reference": "f2a5e2915f8924bd36979a1001edeef7cd455f89", + "shasum": "" + }, + "require": { + "cakephp/cakephp": "~3.1", + "php": ">=5.4", + "robmorgan/phinx": "0.6.5" + }, + "require-dev": { + "cakephp/bake": "@stable", + "phpunit/phpunit": "*" + }, + "suggest": { + "cakephp/bake": "Required if you want to generate migrations." + }, + "type": "cakephp-plugin", + "autoload": { + "psr-4": { + "Migrations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/migrations/graphs/contributors" + } + ], + "description": "Database Migration plugin for CakePHP 3.0 based on Phinx", + "homepage": "https://github.com/cakephp/migrations", + "keywords": [ + "cakephp", + "migrations" + ], + "time": "2017-01-07 16:45:31" + }, + { + "name": "cakephp/plugin-installer", + "version": "0.0.15", + "source": { + "type": "git", + "url": "https://github.com/cakephp/plugin-installer.git", + "reference": "8e84898b44df50e88b5109bb7d4d28de845e9bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/plugin-installer/zipball/8e84898b44df50e88b5109bb7d4d28de845e9bf8", + "reference": "8e84898b44df50e88b5109bb7d4d28de845e9bf8", + "shasum": "" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "dev-master", + "composer/composer": "1.0.*@dev" + }, + "type": "composer-installer", + "extra": { + "class": "Cake\\Composer\\Installer\\PluginInstaller" + }, + "autoload": { + "psr-4": { + "Cake\\Composer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "http://cakephp.org" + } + ], + "description": "A composer installer for CakePHP 3.0+ plugins.", + "time": "2016-04-28 03:01:34" + }, + { + "name": "mobiledetect/mobiledetectlib", + "version": "2.8.24", + "source": { + "type": "git", + "url": "https://github.com/serbanghita/Mobile-Detect.git", + "reference": "cdf8f8efaf993bc687e78e4622f5eebd0b8b3bf3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/cdf8f8efaf993bc687e78e4622f5eebd0b8b3bf3", + "reference": "cdf8f8efaf993bc687e78e4622f5eebd0b8b3bf3", + "shasum": "" + }, + "require": { + "php": ">=5.0.0" + }, + "require-dev": { + "codeclimate/php-test-reporter": "dev-master", + "johnkary/phpunit-speedtrap": "~1.0@dev", + "phpunit/phpunit": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "Mobile_Detect.php" + ], + "psr-0": { + "Detection": "namespaced/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Serban Ghita", + "email": "serbanghita@gmail.com", + "homepage": "http://mobiledetect.net", + "role": "Developer" + } + ], + "description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.", + "homepage": "https://github.com/serbanghita/Mobile-Detect", + "keywords": [ + "detect mobile devices", + "mobile", + "mobile detect", + "mobile detector", + "php mobile detect" + ], + "time": "2016-11-11 14:56:25" + }, + { + "name": "psr/log", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Psr\\Log\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2012-12-21 11:40:51" + }, + { + "name": "robmorgan/phinx", + "version": "v0.6.5", + "source": { + "type": "git", + "url": "https://github.com/robmorgan/phinx.git", + "reference": "6943cb4bb78bf9d3964967a032220b7c793b97b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/robmorgan/phinx/zipball/6943cb4bb78bf9d3964967a032220b7c793b97b7", + "reference": "6943cb4bb78bf9d3964967a032220b7c793b97b7", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "symfony/config": "~2.8|~3.0", + "symfony/console": "~2.8|~3.0", + "symfony/yaml": "~2.8|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.26|^5.0" + }, + "bin": [ + "bin/phinx" + ], + "type": "library", + "autoload": { + "psr-4": { + "Phinx\\": "src/Phinx" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com", + "homepage": "http://shadowhand.me", + "role": "Developer" + }, + { + "name": "Rob Morgan", + "email": "robbym@gmail.com", + "homepage": "https://robmorgan.id.au", + "role": "Lead Developer" + }, + { + "name": "Richard Quadling", + "email": "rquadling@gmail.com", + "role": "Developer" + } + ], + "description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.", + "homepage": "https://phinx.org", + "keywords": [ + "database", + "database migrations", + "db", + "migrations", + "phinx" + ], + "time": "2016-10-27 10:16:12" + }, + { + "name": "symfony/config", + "version": "v3.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "2ffa7b84d647b8be1788d46b44e438cb3d62056c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/2ffa7b84d647b8be1788d46b44e438cb3d62056c", + "reference": "2ffa7b84d647b8be1788d46b44e438cb3d62056c", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/filesystem": "~2.8|~3.0" + }, + "require-dev": { + "symfony/yaml": "~3.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Config Component", + "homepage": "https://symfony.com", + "time": "2017-02-06 12:04:21" + }, + { + "name": "symfony/console", + "version": "v3.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "7a8405a9fc175f87fed8a3c40856b0d866d61936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/7a8405a9fc175f87fed8a3c40856b0d866d61936", + "reference": "7a8405a9fc175f87fed8a3c40856b0d866d61936", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/debug": "~2.8|~3.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/filesystem": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/filesystem": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2017-02-06 12:04:21" + }, + { + "name": "symfony/debug", + "version": "v3.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "b4d9818f127c60ce21ed62c395da7df868dc8477" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/b4d9818f127c60ce21ed62c395da7df868dc8477", + "reference": "b4d9818f127c60ce21ed62c395da7df868dc8477", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/class-loader": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2017-01-28 02:37:08" + }, + { + "name": "symfony/filesystem", + "version": "v3.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "a0c6ef2dc78d33b58d91d3a49f49797a184d06f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/a0c6ef2dc78d33b58d91d3a49f49797a184d06f4", + "reference": "a0c6ef2dc78d33b58d91d3a49f49797a184d06f4", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2017-01-08 20:47:33" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2016-11-14 01:06:16" + }, + { + "name": "symfony/yaml", + "version": "v3.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "e1718c6bf57e1efbb8793ada951584b2ab27775b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e1718c6bf57e1efbb8793ada951584b2ab27775b", + "reference": "e1718c6bf57e1efbb8793ada951584b2ab27775b", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/console": "~2.8|~3.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2017-01-21 17:06:35" + } + ], + "packages-dev": [ + { + "name": "cakephp/bake", + "version": "1.2.14", + "source": { + "type": "git", + "url": "https://github.com/cakephp/bake.git", + "reference": "24ef0cc781b529a8566cce4ecaf3aeaa1f08efd4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/bake/zipball/24ef0cc781b529a8566cce4ecaf3aeaa1f08efd4", + "reference": "24ef0cc781b529a8566cce4ecaf3aeaa1f08efd4", + "shasum": "" + }, + "require": { + "cakephp/cakephp": ">=3.2.0 <4.0.0", + "cakephp/plugin-installer": "*", + "php": ">=5.5.9" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "^2.1", + "phpunit/phpunit": "*" + }, + "type": "cakephp-plugin", + "autoload": { + "psr-4": { + "Bake\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/bake/graphs/contributors" + } + ], + "description": "Bake plugin for CakePHP 3.0", + "homepage": "https://github.com/cakephp/bake", + "keywords": [ + "bake", + "cakephp" + ], + "time": "2017-01-31 00:59:03" + }, + { + "name": "cakephp/debug_kit", + "version": "3.9.0", + "source": { + "type": "git", + "url": "https://github.com/cakephp/debug_kit.git", + "reference": "bd52bde720cb59db9b54f64794b4ab242a6a588d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/debug_kit/zipball/bd52bde720cb59db9b54f64794b4ab242a6a588d", + "reference": "bd52bde720cb59db9b54f64794b4ab242a6a588d", + "shasum": "" + }, + "require": { + "cakephp/cakephp": ">=3.1.0 <4.0", + "cakephp/plugin-installer": "*", + "composer/composer": "^1.3", + "jdorn/sql-formatter": "~1.2" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "~2.1", + "phpunit/phpunit": "<6.0" + }, + "suggest": { + "ext-sqlite": "DebugKit needs to store panel data in a database. SQLite is simple and easy to use." + }, + "type": "cakephp-plugin", + "autoload": { + "psr-4": { + "DebugKit\\": "src", + "DebugKit\\Test\\Fixture\\": "tests\\Fixture" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Story", + "homepage": "http://mark-story.com", + "role": "Author" + }, + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/debug_kit/graphs/contributors" + } + ], + "description": "CakePHP Debug Kit", + "homepage": "https://github.com/cakephp/debug_kit", + "keywords": [ + "cakephp", + "debug", + "kit" + ], + "time": "2017-02-11 21:15:54" + }, + { + "name": "composer/ca-bundle", + "version": "1.0.6", + "source": { + "type": "git", + "url": "https://github.com/composer/ca-bundle.git", + "reference": "a795611394b3c05164fd0eb291b492b39339cba4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/a795611394b3c05164fd0eb291b492b39339cba4", + "reference": "a795611394b3c05164fd0eb291b492b39339cba4", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^5.3.2 || ^7.0" + }, + "require-dev": { + "psr/log": "^1.0", + "symfony/process": "^2.5 || ^3.0" + }, + "suggest": { + "symfony/process": "This is necessary to reliably check whether openssl_x509_parse is vulnerable on older php versions, but can be ignored on PHP 5.5.6+" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\CaBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "keywords": [ + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" + ], + "time": "2016-11-02 18:11:27" + }, + { + "name": "composer/composer", + "version": "1.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/composer.git", + "reference": "e7569edb4a5eadcbb2e4ad5ed753282260f281df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/composer/zipball/e7569edb4a5eadcbb2e4ad5ed753282260f281df", + "reference": "e7569edb4a5eadcbb2e4ad5ed753282260f281df", + "shasum": "" + }, + "require": { + "composer/ca-bundle": "^1.0", + "composer/semver": "^1.0", + "composer/spdx-licenses": "^1.0", + "justinrainbow/json-schema": "^1.6 || ^2.0 || ^3.0 || ^4.0", + "php": "^5.3.2 || ^7.0", + "psr/log": "^1.0", + "seld/cli-prompt": "^1.0", + "seld/jsonlint": "^1.4", + "seld/phar-utils": "^1.0", + "symfony/console": "^2.7 || ^3.0", + "symfony/filesystem": "^2.7 || ^3.0", + "symfony/finder": "^2.7 || ^3.0", + "symfony/process": "^2.7 || ^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.5 || ^5.0.5", + "phpunit/phpunit-mock-objects": "^2.3 || ^3.0" + }, + "suggest": { + "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", + "ext-zip": "Enabling the zip extension allows you to unzip archives", + "ext-zlib": "Allow gzip compression of HTTP requests" + }, + "bin": [ + "bin/composer" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\": "src/Composer" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.", + "homepage": "https://getcomposer.org/", + "keywords": [ + "autoload", + "dependency", + "package" + ], + "time": "2017-01-27 17:23:42" + }, + { + "name": "composer/semver", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573", + "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.5 || ^5.0.5", + "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "time": "2016-08-30 16:08:34" + }, + { + "name": "composer/spdx-licenses", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/composer/spdx-licenses.git", + "reference": "96c6a07b05b716e89a44529d060bc7f5c263cb13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/96c6a07b05b716e89a44529d060bc7f5c263cb13", + "reference": "96c6a07b05b716e89a44529d060bc7f5c263cb13", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.5 || ^5.0.5", + "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Spdx\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "SPDX licenses list and validation library.", + "keywords": [ + "license", + "spdx", + "validator" + ], + "time": "2016-09-28 07:17:45" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "0.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/265b8593498b997dc2d31e75b89f053b5cc9621a", + "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "@stable" + }, + "type": "project", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "time": "2014-10-24 07:27:01" + }, + { + "name": "jakub-onderka/php-console-color", + "version": "0.1", + "source": { + "type": "git", + "url": "https://github.com/JakubOnderka/PHP-Console-Color.git", + "reference": "e0b393dacf7703fc36a4efc3df1435485197e6c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Color/zipball/e0b393dacf7703fc36a4efc3df1435485197e6c1", + "reference": "e0b393dacf7703fc36a4efc3df1435485197e6c1", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "jakub-onderka/php-code-style": "1.0", + "jakub-onderka/php-parallel-lint": "0.*", + "jakub-onderka/php-var-dump-check": "0.*", + "phpunit/phpunit": "3.7.*", + "squizlabs/php_codesniffer": "1.*" + }, + "type": "library", + "autoload": { + "psr-0": { + "JakubOnderka\\PhpConsoleColor": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "jakub.onderka@gmail.com", + "homepage": "http://www.acci.cz" + } + ], + "time": "2014-04-08 15:00:19" + }, + { + "name": "jakub-onderka/php-console-highlighter", + "version": "v0.3.2", + "source": { + "type": "git", + "url": "https://github.com/JakubOnderka/PHP-Console-Highlighter.git", + "reference": "7daa75df45242c8d5b75a22c00a201e7954e4fb5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Highlighter/zipball/7daa75df45242c8d5b75a22c00a201e7954e4fb5", + "reference": "7daa75df45242c8d5b75a22c00a201e7954e4fb5", + "shasum": "" + }, + "require": { + "jakub-onderka/php-console-color": "~0.1", + "php": ">=5.3.0" + }, + "require-dev": { + "jakub-onderka/php-code-style": "~1.0", + "jakub-onderka/php-parallel-lint": "~0.5", + "jakub-onderka/php-var-dump-check": "~0.1", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~1.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JakubOnderka\\PhpConsoleHighlighter": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "acci@acci.cz", + "homepage": "http://www.acci.cz/" + } + ], + "time": "2015-04-20 18:58:01" + }, + { + "name": "jdorn/sql-formatter", + "version": "v1.2.17", + "source": { + "type": "git", + "url": "https://github.com/jdorn/sql-formatter.git", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/64990d96e0959dff8e059dfcdc1af130728d92bc", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "lib" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeremy Dorn", + "email": "jeremy@jeremydorn.com", + "homepage": "http://jeremydorn.com/" + } + ], + "description": "a PHP SQL highlighting library", + "homepage": "https://github.com/jdorn/sql-formatter/", + "keywords": [ + "highlight", + "sql" + ], + "time": "2014-01-12 16:20:24" + }, + { + "name": "justinrainbow/json-schema", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "d39c56a46b3ebe1f3696479966cd2b9f50aaa24f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/d39c56a46b3ebe1f3696479966cd2b9f50aaa24f", + "reference": "d39c56a46b3ebe1f3696479966cd2b9f50aaa24f", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "json-schema/json-schema-test-suite": "1.2.0", + "phpdocumentor/phpdocumentor": "~2", + "phpunit/phpunit": "^4.8.22" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "time": "2016-12-22 16:43:46" + }, + { + "name": "nikic/php-parser", + "version": "v3.0.4", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "0bf561dfe75ba80441c22adecc0529056671a7d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/0bf561dfe75ba80441c22adecc0529056671a7d2", + "reference": "0bf561dfe75ba80441c22adecc0529056671a7d2", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "~4.0|~5.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2017-02-10 20:20:03" + }, + { + "name": "psy/psysh", + "version": "v0.8.1", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "701e8a1cc426ee170f1296f5d9f6b8a26ad25c4a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/701e8a1cc426ee170f1296f5d9f6b8a26ad25c4a", + "reference": "701e8a1cc426ee170f1296f5d9f6b8a26ad25c4a", + "shasum": "" + }, + "require": { + "dnoegel/php-xdg-base-dir": "0.1", + "jakub-onderka/php-console-highlighter": "0.3.*", + "nikic/php-parser": "~1.3|~2.0|~3.0", + "php": ">=5.3.9", + "symfony/console": "~2.3.10|^2.4.2|~3.0", + "symfony/var-dumper": "~2.7|~3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~1.11", + "hoa/console": "~3.16|~1.14", + "phpunit/phpunit": "~4.4|~5.0", + "symfony/finder": "~2.1|~3.0" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.", + "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history.", + "hoa/console": "A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-develop": "0.9.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psy/functions.php" + ], + "psr-4": { + "Psy\\": "src/Psy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "http://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "time": "2017-01-15 17:54:13" + }, + { + "name": "seld/cli-prompt", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/cli-prompt.git", + "reference": "8cbe10923cae5bcd7c5a713f6703fc4727c8c1b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/cli-prompt/zipball/8cbe10923cae5bcd7c5a713f6703fc4727c8c1b4", + "reference": "8cbe10923cae5bcd7c5a713f6703fc4727c8c1b4", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\CliPrompt\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Allows you to prompt for user input on the command line, and optionally hide the characters they type", + "keywords": [ + "cli", + "console", + "hidden", + "input", + "prompt" + ], + "time": "2016-04-18 09:31:41" + }, + { + "name": "seld/jsonlint", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "19495c181d6d53a0a13414154e52817e3b504189" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/19495c181d6d53a0a13414154e52817e3b504189", + "reference": "19495c181d6d53a0a13414154e52817e3b504189", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "time": "2016-11-14 17:59:58" + }, + { + "name": "seld/phar-utils", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/phar-utils.git", + "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/7009b5139491975ef6486545a39f3e6dad5ac30a", + "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\PharUtils\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "PHAR file format utilities, for when PHP phars you up", + "keywords": [ + "phra" + ], + "time": "2015-10-13 18:44:15" + }, + { + "name": "symfony/finder", + "version": "v3.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "8c71141cae8e2957946b403cc71a67213c0380d6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/8c71141cae8e2957946b403cc71a67213c0380d6", + "reference": "8c71141cae8e2957946b403cc71a67213c0380d6", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2017-01-02 20:32:22" + }, + { + "name": "symfony/process", + "version": "v3.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "32646a7cf53f3956c76dcb5c82555224ae321858" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/32646a7cf53f3956c76dcb5c82555224ae321858", + "reference": "32646a7cf53f3956c76dcb5c82555224ae321858", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2017-02-03 12:11:38" + }, + { + "name": "symfony/var-dumper", + "version": "v3.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "5bb4435a03a4f05c211f4a9a8ee2756965924511" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/5bb4435a03a4f05c211f4a9a8ee2756965924511", + "reference": "5bb4435a03a4f05c211f4a9a8ee2756965924511", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "twig/twig": "~1.20|~2.0" + }, + "suggest": { + "ext-symfony_debug": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony mechanism for exploring and dumping PHP variables", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "time": "2017-01-24 12:58:58" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "psy/psysh": 0 + }, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": ">=5.5.9" + }, + "platform-dev": [] +} diff --git a/cake-3.2/config/app.default.php b/cake-3.2/config/app.default.php new file mode 100644 index 000000000..2f2245b0c --- /dev/null +++ b/cake-3.2/config/app.default.php @@ -0,0 +1,342 @@ + filter_var(env('DEBUG', true), FILTER_VALIDATE_BOOLEAN), + + /** + * Configure basic information about the application. + * + * - namespace - The namespace to find app classes under. + * - encoding - The encoding used for HTML + database connections. + * - base - The base directory the app resides in. If false this + * will be auto detected. + * - dir - Name of app directory. + * - webroot - The webroot directory. + * - wwwRoot - The file path to webroot. + * - baseUrl - To configure CakePHP to *not* use mod_rewrite and to + * use CakePHP pretty URLs, remove these .htaccess + * files: + * /.htaccess + * /webroot/.htaccess + * And uncomment the baseUrl key below. + * - fullBaseUrl - A base URL to use for absolute links. + * - imageBaseUrl - Web path to the public images directory under webroot. + * - cssBaseUrl - Web path to the public css directory under webroot. + * - jsBaseUrl - Web path to the public js directory under webroot. + * - paths - Configure paths for non class based resources. Supports the + * `plugins`, `templates`, `locales` subkeys, which allow the definition of + * paths for plugins, view templates and locale files respectively. + */ + 'App' => [ + 'namespace' => 'App', + 'encoding' => env('APP_ENCODING', 'UTF-8'), + 'defaultLocale' => env('APP_DEFAULT_LOCALE', 'en_US'), + 'base' => false, + 'dir' => 'src', + 'webroot' => 'webroot', + 'wwwRoot' => WWW_ROOT, + // 'baseUrl' => env('SCRIPT_NAME'), + 'fullBaseUrl' => false, + 'imageBaseUrl' => 'img/', + 'cssBaseUrl' => 'css/', + 'jsBaseUrl' => 'js/', + 'paths' => [ + 'plugins' => [ROOT . DS . 'plugins' . DS], + 'templates' => [APP . 'Template' . DS], + 'locales' => [APP . 'Locale' . DS], + ], + ], + + /** + * Security and encryption configuration + * + * - salt - A random string used in security hashing methods. + * The salt value is also used as the encryption key. + * You should treat it as extremely sensitive data. + */ + 'Security' => [ + 'salt' => env('SECURITY_SALT', '__SALT__'), + ], + + /** + * Apply timestamps with the last modified time to static assets (js, css, images). + * Will append a querystring parameter containing the time the file was modified. + * This is useful for busting browser caches. + * + * Set to true to apply timestamps when debug is true. Set to 'force' to always + * enable timestamping regardless of debug value. + */ + 'Asset' => [ + // 'timestamp' => true, + ], + + /** + * Configure the cache adapters. + */ + 'Cache' => [ + 'default' => [ + 'className' => 'File', + 'path' => CACHE, + 'url' => env('CACHE_DEFAULT_URL', null), + ], + + /** + * Configure the cache used for general framework caching. + * Translation cache files are stored with this configuration. + * Duration will be set to '+1 year' in bootstrap.php when debug = false + */ + '_cake_core_' => [ + 'className' => 'File', + 'prefix' => 'myapp_cake_core_', + 'path' => CACHE . 'persistent/', + 'serialize' => true, + 'duration' => '+2 minutes', + 'url' => env('CACHE_CAKECORE_URL', null), + ], + + /** + * Configure the cache for model and datasource caches. This cache + * configuration is used to store schema descriptions, and table listings + * in connections. + * Duration will be set to '+1 year' in bootstrap.php when debug = false + */ + '_cake_model_' => [ + 'className' => 'File', + 'prefix' => 'myapp_cake_model_', + 'path' => CACHE . 'models/', + 'serialize' => true, + 'duration' => '+2 minutes', + 'url' => env('CACHE_CAKEMODEL_URL', null), + ], + ], + + /** + * Configure the Error and Exception handlers used by your application. + * + * By default errors are displayed using Debugger, when debug is true and logged + * by Cake\Log\Log when debug is false. + * + * In CLI environments exceptions will be printed to stderr with a backtrace. + * In web environments an HTML page will be displayed for the exception. + * With debug true, framework errors like Missing Controller will be displayed. + * When debug is false, framework errors will be coerced into generic HTTP errors. + * + * Options: + * + * - `errorLevel` - int - The level of errors you are interested in capturing. + * - `trace` - boolean - Whether or not backtraces should be included in + * logged errors/exceptions. + * - `log` - boolean - Whether or not you want exceptions logged. + * - `exceptionRenderer` - string - The class responsible for rendering + * uncaught exceptions. If you choose a custom class you should place + * the file for that class in src/Error. This class needs to implement a + * render method. + * - `skipLog` - array - List of exceptions to skip for logging. Exceptions that + * extend one of the listed exceptions will also be skipped for logging. + * E.g.: + * `'skipLog' => ['Cake\Network\Exception\NotFoundException', 'Cake\Network\Exception\UnauthorizedException']` + * - `extraFatalErrorMemory` - int - The number of megabytes to increase + * the memory limit by when a fatal error is encountered. This allows + * breathing room to complete logging or error handling. + */ + 'Error' => [ + 'errorLevel' => E_ALL & ~E_DEPRECATED, + 'exceptionRenderer' => 'Cake\Error\ExceptionRenderer', + 'skipLog' => [], + 'log' => true, + 'trace' => true, + ], + + /** + * Email configuration. + * + * By defining transports separately from delivery profiles you can easily + * re-use transport configuration across multiple profiles. + * + * You can specify multiple configurations for production, development and + * testing. + * + * Each transport needs a `className`. Valid options are as follows: + * + * Mail - Send using PHP mail function + * Smtp - Send using SMTP + * Debug - Do not send the email, just return the result + * + * You can add custom transports (or override existing transports) by adding the + * appropriate file to src/Mailer/Transport. Transports should be named + * 'YourTransport.php', where 'Your' is the name of the transport. + */ + 'EmailTransport' => [ + 'default' => [ + 'className' => 'Mail', + // The following keys are used in SMTP transports + 'host' => 'localhost', + 'port' => 25, + 'timeout' => 30, + 'username' => 'user', + 'password' => 'secret', + 'client' => null, + 'tls' => null, + 'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null), + ], + ], + + /** + * Email delivery profiles + * + * Delivery profiles allow you to predefine various properties about email + * messages from your application and give the settings a name. This saves + * duplication across your application and makes maintenance and development + * easier. Each profile accepts a number of keys. See `Cake\Mailer\Email` + * for more information. + */ + 'Email' => [ + 'default' => [ + 'transport' => 'default', + 'from' => 'you@localhost', + //'charset' => 'utf-8', + //'headerCharset' => 'utf-8', + ], + ], + + /** + * Connection information used by the ORM to connect + * to your application's datastores. + * Drivers include Mysql Postgres Sqlite Sqlserver + * See vendor\cakephp\cakephp\src\Database\Driver for complete list + */ + 'Datasources' => [ + 'default' => [ + 'className' => 'Cake\Database\Connection', + 'driver' => 'Cake\Database\Driver\Mysql', + 'persistent' => false, + 'host' => 'localhost', + /** + * CakePHP will use the default DB port based on the driver selected + * MySQL on MAMP uses port 8889, MAMP users will want to uncomment + * the following line and set the port accordingly + */ + //'port' => 'non_standard_port_number', + 'username' => 'my_app', + 'password' => 'secret', + 'database' => 'my_app', + 'encoding' => 'utf8', + 'timezone' => 'UTC', + 'flags' => [], + 'cacheMetadata' => true, + 'log' => false, + + /** + * Set identifier quoting to true if you are using reserved words or + * special characters in your table or column names. Enabling this + * setting will result in queries built using the Query Builder having + * identifiers quoted when creating SQL. It should be noted that this + * decreases performance because each query needs to be traversed and + * manipulated before being executed. + */ + 'quoteIdentifiers' => false, + + /** + * During development, if using MySQL < 5.6, uncommenting the + * following line could boost the speed at which schema metadata is + * fetched from the database. It can also be set directly with the + * mysql configuration directive 'innodb_stats_on_metadata = 0' + * which is the recommended value in production environments + */ + //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'], + + 'url' => env('DATABASE_URL', null), + ], + + /** + * The test connection is used during the test suite. + */ + 'test' => [ + 'className' => 'Cake\Database\Connection', + 'driver' => 'Cake\Database\Driver\Mysql', + 'persistent' => false, + 'host' => 'localhost', + //'port' => 'non_standard_port_number', + 'username' => 'my_app', + 'password' => 'secret', + 'database' => 'test_myapp', + 'encoding' => 'utf8', + 'timezone' => 'UTC', + 'cacheMetadata' => true, + 'quoteIdentifiers' => false, + 'log' => false, + //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'], + 'url' => env('DATABASE_TEST_URL', null), + ], + ], + + /** + * Configures logging options + */ + 'Log' => [ + 'debug' => [ + 'className' => 'Cake\Log\Engine\FileLog', + 'path' => LOGS, + 'file' => 'debug', + 'levels' => ['notice', 'info', 'debug'], + 'url' => env('LOG_DEBUG_URL', null), + ], + 'error' => [ + 'className' => 'Cake\Log\Engine\FileLog', + 'path' => LOGS, + 'file' => 'error', + 'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'], + 'url' => env('LOG_ERROR_URL', null), + ], + ], + + /** + * Session configuration. + * + * Contains an array of settings to use for session configuration. The + * `defaults` key is used to define a default preset to use for sessions, any + * settings declared here will override the settings of the default config. + * + * ## Options + * + * - `cookie` - The name of the cookie to use. Defaults to 'CAKEPHP'. + * - `cookiePath` - The url path for which session cookie is set. Maps to the + * `session.cookie_path` php.ini config. Defaults to base path of app. + * - `timeout` - The time in minutes the session should be valid for. + * Pass 0 to disable checking timeout. + * Please note that php.ini's session.gc_maxlifetime must be equal to or greater + * than the largest Session['timeout'] in all served websites for it to have the + * desired effect. + * - `defaults` - The default configuration set to use as a basis for your session. + * There are four built-in options: php, cake, cache, database. + * - `handler` - Can be used to enable a custom session handler. Expects an + * array with at least the `engine` key, being the name of the Session engine + * class to use for managing the session. CakePHP bundles the `CacheSession` + * and `DatabaseSession` engines. + * - `ini` - An associative array of additional ini values to set. + * + * The built-in `defaults` options are: + * + * - 'php' - Uses settings defined in your php.ini. + * - 'cake' - Saves session files in CakePHP's /tmp directory. + * - 'database' - Uses CakePHP's database sessions. + * - 'cache' - Use the Cache class to save sessions. + * + * To define a custom session handler, save it at src/Network/Session/.php. + * Make sure the class implements PHP's `SessionHandlerInterface` and set + * Session.handler to + * + * To use database sessions, load the SQL file located at config/Schema/sessions.sql + */ + 'Session' => [ + 'defaults' => 'php', + ], +]; diff --git a/cake-3.2/config/app.php b/cake-3.2/config/app.php new file mode 100644 index 000000000..55b826332 --- /dev/null +++ b/cake-3.2/config/app.php @@ -0,0 +1,342 @@ + false, + + /** + * Configure basic information about the application. + * + * - namespace - The namespace to find app classes under. + * - encoding - The encoding used for HTML + database connections. + * - base - The base directory the app resides in. If false this + * will be auto detected. + * - dir - Name of app directory. + * - webroot - The webroot directory. + * - wwwRoot - The file path to webroot. + * - baseUrl - To configure CakePHP to *not* use mod_rewrite and to + * use CakePHP pretty URLs, remove these .htaccess + * files: + * /.htaccess + * /webroot/.htaccess + * And uncomment the baseUrl key below. + * - fullBaseUrl - A base URL to use for absolute links. + * - imageBaseUrl - Web path to the public images directory under webroot. + * - cssBaseUrl - Web path to the public css directory under webroot. + * - jsBaseUrl - Web path to the public js directory under webroot. + * - paths - Configure paths for non class based resources. Supports the + * `plugins`, `templates`, `locales` subkeys, which allow the definition of + * paths for plugins, view templates and locale files respectively. + */ + 'App' => [ + 'namespace' => 'App', + 'encoding' => env('APP_ENCODING', 'UTF-8'), + 'defaultLocale' => env('APP_DEFAULT_LOCALE', 'en_US'), + 'base' => false, + 'dir' => 'src', + 'webroot' => 'webroot', + 'wwwRoot' => WWW_ROOT, + // 'baseUrl' => env('SCRIPT_NAME'), + 'fullBaseUrl' => false, + 'imageBaseUrl' => 'img/', + 'cssBaseUrl' => 'css/', + 'jsBaseUrl' => 'js/', + 'paths' => [ + 'plugins' => [ROOT . DS . 'plugins' . DS], + 'templates' => [APP . 'Template' . DS], + 'locales' => [APP . 'Locale' . DS], + ], + ], + + /** + * Security and encryption configuration + * + * - salt - A random string used in security hashing methods. + * The salt value is also used as the encryption key. + * You should treat it as extremely sensitive data. + */ + 'Security' => [ + 'salt' => env('SECURITY_SALT', '6110aab18271edb4f0ed1157133ca7a9cd0a547186ff01114a9970a3cc13f2c8'), + ], + + /** + * Apply timestamps with the last modified time to static assets (js, css, images). + * Will append a querystring parameter containing the time the file was modified. + * This is useful for busting browser caches. + * + * Set to true to apply timestamps when debug is true. Set to 'force' to always + * enable timestamping regardless of debug value. + */ + 'Asset' => [ + // 'timestamp' => true, + ], + + /** + * Configure the cache adapters. + */ + 'Cache' => [ + 'default' => [ + 'className' => 'File', + 'path' => CACHE, + 'url' => env('CACHE_DEFAULT_URL', null), + ], + + /** + * Configure the cache used for general framework caching. + * Translation cache files are stored with this configuration. + * Duration will be set to '+1 year' in bootstrap.php when debug = false + */ + '_cake_core_' => [ + 'className' => 'File', + 'prefix' => 'myapp_cake_core_', + 'path' => CACHE . 'persistent/', + 'serialize' => true, + 'duration' => '+2 minutes', + 'url' => env('CACHE_CAKECORE_URL', null), + ], + + /** + * Configure the cache for model and datasource caches. This cache + * configuration is used to store schema descriptions, and table listings + * in connections. + * Duration will be set to '+1 year' in bootstrap.php when debug = false + */ + '_cake_model_' => [ + 'className' => 'File', + 'prefix' => 'myapp_cake_model_', + 'path' => CACHE . 'models/', + 'serialize' => true, + 'duration' => '+2 minutes', + 'url' => env('CACHE_CAKEMODEL_URL', null), + ], + ], + + /** + * Configure the Error and Exception handlers used by your application. + * + * By default errors are displayed using Debugger, when debug is true and logged + * by Cake\Log\Log when debug is false. + * + * In CLI environments exceptions will be printed to stderr with a backtrace. + * In web environments an HTML page will be displayed for the exception. + * With debug true, framework errors like Missing Controller will be displayed. + * When debug is false, framework errors will be coerced into generic HTTP errors. + * + * Options: + * + * - `errorLevel` - int - The level of errors you are interested in capturing. + * - `trace` - boolean - Whether or not backtraces should be included in + * logged errors/exceptions. + * - `log` - boolean - Whether or not you want exceptions logged. + * - `exceptionRenderer` - string - The class responsible for rendering + * uncaught exceptions. If you choose a custom class you should place + * the file for that class in src/Error. This class needs to implement a + * render method. + * - `skipLog` - array - List of exceptions to skip for logging. Exceptions that + * extend one of the listed exceptions will also be skipped for logging. + * E.g.: + * `'skipLog' => ['Cake\Network\Exception\NotFoundException', 'Cake\Network\Exception\UnauthorizedException']` + * - `extraFatalErrorMemory` - int - The number of megabytes to increase + * the memory limit by when a fatal error is encountered. This allows + * breathing room to complete logging or error handling. + */ + 'Error' => [ + 'errorLevel' => E_ALL & ~E_DEPRECATED, + 'exceptionRenderer' => 'Cake\Error\ExceptionRenderer', + 'skipLog' => [], + 'log' => true, + 'trace' => true, + ], + + /** + * Email configuration. + * + * By defining transports separately from delivery profiles you can easily + * re-use transport configuration across multiple profiles. + * + * You can specify multiple configurations for production, development and + * testing. + * + * Each transport needs a `className`. Valid options are as follows: + * + * Mail - Send using PHP mail function + * Smtp - Send using SMTP + * Debug - Do not send the email, just return the result + * + * You can add custom transports (or override existing transports) by adding the + * appropriate file to src/Mailer/Transport. Transports should be named + * 'YourTransport.php', where 'Your' is the name of the transport. + */ + 'EmailTransport' => [ + 'default' => [ + 'className' => 'Mail', + // The following keys are used in SMTP transports + 'host' => 'localhost', + 'port' => 25, + 'timeout' => 30, + 'username' => 'user', + 'password' => 'secret', + 'client' => null, + 'tls' => null, + 'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null), + ], + ], + + /** + * Email delivery profiles + * + * Delivery profiles allow you to predefine various properties about email + * messages from your application and give the settings a name. This saves + * duplication across your application and makes maintenance and development + * easier. Each profile accepts a number of keys. See `Cake\Mailer\Email` + * for more information. + */ + 'Email' => [ + 'default' => [ + 'transport' => 'default', + 'from' => 'you@localhost', + //'charset' => 'utf-8', + //'headerCharset' => 'utf-8', + ], + ], + + /** + * Connection information used by the ORM to connect + * to your application's datastores. + * Drivers include Mysql Postgres Sqlite Sqlserver + * See vendor\cakephp\cakephp\src\Database\Driver for complete list + */ + 'Datasources' => [ + 'default' => [ + 'className' => 'Cake\Database\Connection', + 'driver' => 'Cake\Database\Driver\Mysql', + 'persistent' => false, + 'host' => 'localhost', + /** + * CakePHP will use the default DB port based on the driver selected + * MySQL on MAMP uses port 8889, MAMP users will want to uncomment + * the following line and set the port accordingly + */ + //'port' => 'non_standard_port_number', + 'username' => 'my_app', + 'password' => 'secret', + 'database' => 'my_app', + 'encoding' => 'utf8', + 'timezone' => 'UTC', + 'flags' => [], + 'cacheMetadata' => true, + 'log' => false, + + /** + * Set identifier quoting to true if you are using reserved words or + * special characters in your table or column names. Enabling this + * setting will result in queries built using the Query Builder having + * identifiers quoted when creating SQL. It should be noted that this + * decreases performance because each query needs to be traversed and + * manipulated before being executed. + */ + 'quoteIdentifiers' => false, + + /** + * During development, if using MySQL < 5.6, uncommenting the + * following line could boost the speed at which schema metadata is + * fetched from the database. It can also be set directly with the + * mysql configuration directive 'innodb_stats_on_metadata = 0' + * which is the recommended value in production environments + */ + //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'], + + 'url' => env('DATABASE_URL', null), + ], + + /** + * The test connection is used during the test suite. + */ + 'test' => [ + 'className' => 'Cake\Database\Connection', + 'driver' => 'Cake\Database\Driver\Mysql', + 'persistent' => false, + 'host' => 'localhost', + //'port' => 'non_standard_port_number', + 'username' => 'my_app', + 'password' => 'secret', + 'database' => 'test_myapp', + 'encoding' => 'utf8', + 'timezone' => 'UTC', + 'cacheMetadata' => true, + 'quoteIdentifiers' => false, + 'log' => false, + //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'], + 'url' => env('DATABASE_TEST_URL', null), + ], + ], + + /** + * Configures logging options + */ + 'Log' => [ + 'debug' => [ + 'className' => 'Cake\Log\Engine\FileLog', + 'path' => LOGS, + 'file' => 'debug', + 'levels' => ['notice', 'info', 'debug'], + 'url' => env('LOG_DEBUG_URL', null), + ], + 'error' => [ + 'className' => 'Cake\Log\Engine\FileLog', + 'path' => LOGS, + 'file' => 'error', + 'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'], + 'url' => env('LOG_ERROR_URL', null), + ], + ], + + /** + * Session configuration. + * + * Contains an array of settings to use for session configuration. The + * `defaults` key is used to define a default preset to use for sessions, any + * settings declared here will override the settings of the default config. + * + * ## Options + * + * - `cookie` - The name of the cookie to use. Defaults to 'CAKEPHP'. + * - `cookiePath` - The url path for which session cookie is set. Maps to the + * `session.cookie_path` php.ini config. Defaults to base path of app. + * - `timeout` - The time in minutes the session should be valid for. + * Pass 0 to disable checking timeout. + * Please note that php.ini's session.gc_maxlifetime must be equal to or greater + * than the largest Session['timeout'] in all served websites for it to have the + * desired effect. + * - `defaults` - The default configuration set to use as a basis for your session. + * There are four built-in options: php, cake, cache, database. + * - `handler` - Can be used to enable a custom session handler. Expects an + * array with at least the `engine` key, being the name of the Session engine + * class to use for managing the session. CakePHP bundles the `CacheSession` + * and `DatabaseSession` engines. + * - `ini` - An associative array of additional ini values to set. + * + * The built-in `defaults` options are: + * + * - 'php' - Uses settings defined in your php.ini. + * - 'cake' - Saves session files in CakePHP's /tmp directory. + * - 'database' - Uses CakePHP's database sessions. + * - 'cache' - Use the Cache class to save sessions. + * + * To define a custom session handler, save it at src/Network/Session/.php. + * Make sure the class implements PHP's `SessionHandlerInterface` and set + * Session.handler to + * + * To use database sessions, load the SQL file located at config/Schema/sessions.sql + */ + 'Session' => [ + 'defaults' => 'php', + ], +]; diff --git a/cake-3.2/config/bootstrap.php b/cake-3.2/config/bootstrap.php new file mode 100644 index 000000000..e4f77f431 --- /dev/null +++ b/cake-3.2/config/bootstrap.php @@ -0,0 +1,222 @@ +getMessage() . "\n"); +} + +// Load an environment local configuration file. +// You can use a file like app_local.php to provide local overrides to your +// shared configuration. +//Configure::load('app_local', 'default'); + +// When debug = false the metadata cache should last +// for a very very long time, as we don't want +// to refresh the cache while users are doing requests. +if (!Configure::read('debug')) { + Configure::write('Cache._cake_model_.duration', '+1 years'); + Configure::write('Cache._cake_core_.duration', '+1 years'); +} + +/** + * Set server timezone to UTC. You can change it to another timezone of your + * choice but using UTC makes time calculations / conversions easier. + */ +date_default_timezone_set('UTC'); + +/** + * Configure the mbstring extension to use the correct encoding. + */ +mb_internal_encoding(Configure::read('App.encoding')); + +/** + * Set the default locale. This controls how dates, number and currency is + * formatted and sets the default language to use for translations. + */ +ini_set('intl.default_locale', Configure::read('App.defaultLocale')); + +/** + * Register application error and exception handlers. + */ +$isCli = PHP_SAPI === 'cli'; +if ($isCli) { + (new ConsoleErrorHandler(Configure::read('Error')))->register(); +} else { + (new ErrorHandler(Configure::read('Error')))->register(); +} + +// Include the CLI bootstrap overrides. +if ($isCli) { + require __DIR__ . '/bootstrap_cli.php'; +} + +/** + * Set the full base URL. + * This URL is used as the base of all absolute links. + * + * If you define fullBaseUrl in your config file you can remove this. + */ +if (!Configure::read('App.fullBaseUrl')) { + $s = null; + if (env('HTTPS')) { + $s = 's'; + } + + $httpHost = env('HTTP_HOST'); + if (isset($httpHost)) { + Configure::write('App.fullBaseUrl', 'http' . $s . '://' . $httpHost); + } + unset($httpHost, $s); +} + +Cache::config(Configure::consume('Cache')); +ConnectionManager::config(Configure::consume('Datasources')); +Email::configTransport(Configure::consume('EmailTransport')); +Email::config(Configure::consume('Email')); +Log::config(Configure::consume('Log')); +Security::salt(Configure::consume('Security.salt')); + +/** + * The default crypto extension in 3.0 is OpenSSL. + * If you are migrating from 2.x uncomment this code to + * use a more compatible Mcrypt based implementation + */ +//Security::engine(new \Cake\Utility\Crypto\Mcrypt()); + +/** + * Setup detectors for mobile and tablet. + */ +Request::addDetector('mobile', function ($request) { + $detector = new \Detection\MobileDetect(); + return $detector->isMobile(); +}); +Request::addDetector('tablet', function ($request) { + $detector = new \Detection\MobileDetect(); + return $detector->isTablet(); +}); + +/** + * Custom Inflector rules, can be set to correctly pluralize or singularize + * table, model, controller names or whatever other string is passed to the + * inflection functions. + * + * Inflector::rules('plural', ['/^(inflect)or$/i' => '\1ables']); + * Inflector::rules('irregular', ['red' => 'redlings']); + * Inflector::rules('uninflected', ['dontinflectme']); + * Inflector::rules('transliteration', ['/å/' => 'aa']); + */ + +/** + * Plugins need to be loaded manually, you can either load them one by one or all of them in a single call + * Uncomment one of the lines below, as you need. make sure you read the documentation on Plugin to use more + * advanced ways of loading plugins + * + * Plugin::loadAll(); // Loads all plugins at once + * Plugin::load('Migrations'); //Loads a single plugin named Migrations + * + */ + +Plugin::load('Migrations'); + +// Only try to load DebugKit in development mode +// Debug Kit should not be installed on a production system +if (Configure::read('debug')) { + Plugin::load('DebugKit', ['bootstrap' => true]); +} + +/** + * Connect middleware/dispatcher filters. + */ +DispatcherFactory::add('Asset'); +DispatcherFactory::add('Routing'); +DispatcherFactory::add('ControllerFactory'); + +/** + * Enable default locale format parsing. + * This is needed for matching the auto-localized string output of Time() class when parsing dates. + * + * Also enable immutable time objects in the ORM. + */ +Type::build('time') + ->useImmutable() + ->useLocaleParser(); +Type::build('date') + ->useImmutable() + ->useLocaleParser(); +Type::build('datetime') + ->useImmutable() + ->useLocaleParser(); diff --git a/cake-3.2/config/bootstrap_cli.php b/cake-3.2/config/bootstrap_cli.php new file mode 100644 index 000000000..f442d88fe --- /dev/null +++ b/cake-3.2/config/bootstrap_cli.php @@ -0,0 +1,36 @@ +connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']); + + /** + * ...and connect the rest of 'Pages' controller's URLs. + */ + $routes->connect('/pages/*', ['controller' => 'Pages', 'action' => 'display']); + + /** + * Connect catchall routes for all controllers. + * + * Using the argument `DashedRoute`, the `fallbacks` method is a shortcut for + * `$routes->connect('/:controller', ['action' => 'index'], ['routeClass' => 'DashedRoute']);` + * `$routes->connect('/:controller/:action/*', [], ['routeClass' => 'DashedRoute']);` + * + * Any route class can be used with this method, such as: + * - DashedRoute + * - InflectedRoute + * - Route + * - Or your own route class + * + * You can remove these routes once you've connected the + * routes you want in your application. + */ + $routes->fallbacks('DashedRoute'); +}); + +/** + * Load all plugin routes. See the Plugin documentation on + * how to customize the loading of plugin routes. + */ +Plugin::routes(); diff --git a/cake-3.2/config/schema/i18n.sql b/cake-3.2/config/schema/i18n.sql new file mode 100644 index 000000000..47cf171da --- /dev/null +++ b/cake-3.2/config/schema/i18n.sql @@ -0,0 +1,18 @@ +# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +# +# Licensed under The MIT License +# For full copyright and license information, please see the LICENSE.txt +# Redistributions of files must retain the above copyright notice. +# MIT License (http://www.opensource.org/licenses/mit-license.php) + +CREATE TABLE i18n ( + id int NOT NULL auto_increment, + locale varchar(6) NOT NULL, + model varchar(255) NOT NULL, + foreign_key int(10) NOT NULL, + field varchar(255) NOT NULL, + content text, + PRIMARY KEY (id), + UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field), + INDEX I18N_FIELD(model, foreign_key, field) +); diff --git a/cake-3.2/config/schema/sessions.sql b/cake-3.2/config/schema/sessions.sql new file mode 100644 index 000000000..c0067c7a0 --- /dev/null +++ b/cake-3.2/config/schema/sessions.sql @@ -0,0 +1,13 @@ +# Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +# +# Licensed under The MIT License +# For full copyright and license information, please see the LICENSE.txt +# Redistributions of files must retain the above copyright notice. +# MIT License (http://www.opensource.org/licenses/mit-license.php) + +CREATE TABLE sessions ( + id varchar(40) NOT NULL default '', + data text, + expires INT(11) NOT NULL, + PRIMARY KEY (id) +); diff --git a/cake-3.2/index.php b/cake-3.2/index.php new file mode 100644 index 000000000..fc5e39ccb --- /dev/null +++ b/cake-3.2/index.php @@ -0,0 +1,16 @@ + + + + + + + + + + + ./tests/TestCase + + + + + + + + + + + + + + + + + ./src/ + ./plugins/*/src/ + + + diff --git a/fuel-1.8-dev/public/assets/fonts/index.html b/cake-3.2/plugins/empty similarity index 100% rename from fuel-1.8-dev/public/assets/fonts/index.html rename to cake-3.2/plugins/empty diff --git a/cake-3.2/src/Console/Installer.php b/cake-3.2/src/Console/Installer.php new file mode 100644 index 000000000..9a09e9f54 --- /dev/null +++ b/cake-3.2/src/Console/Installer.php @@ -0,0 +1,192 @@ +getIO(); + + $rootDir = dirname(dirname(__DIR__)); + + static::createAppConfig($rootDir, $io); + static::createWritableDirectories($rootDir, $io); + + // ask if the permissions should be changed + if ($io->isInteractive()) { + $validator = function ($arg) { + if (in_array($arg, ['Y', 'y', 'N', 'n'])) { + return $arg; + } + throw new Exception('This is not a valid answer. Please choose Y or n.'); + }; + $setFolderPermissions = $io->askAndValidate( + 'Set Folder Permissions ? (Default to Y) [Y,n]? ', + $validator, + 10, + 'Y' + ); + + if (in_array($setFolderPermissions, ['Y', 'y'])) { + static::setFolderPermissions($rootDir, $io); + } + } else { + static::setFolderPermissions($rootDir, $io); + } + + static::setSecuritySalt($rootDir, $io); + + if (class_exists('\Cake\Codeception\Console\Installer')) { + \Cake\Codeception\Console\Installer::customizeCodeceptionBinary($event); + } + } + + /** + * Create the config/app.php file if it does not exist. + * + * @param string $dir The application's root directory. + * @param \Composer\IO\IOInterface $io IO interface to write to console. + * @return void + */ + public static function createAppConfig($dir, $io) + { + $appConfig = $dir . '/config/app.php'; + $defaultConfig = $dir . '/config/app.default.php'; + if (!file_exists($appConfig)) { + copy($defaultConfig, $appConfig); + $io->write('Created `config/app.php` file'); + } + } + + /** + * Create the `logs` and `tmp` directories. + * + * @param string $dir The application's root directory. + * @param \Composer\IO\IOInterface $io IO interface to write to console. + * @return void + */ + public static function createWritableDirectories($dir, $io) + { + $paths = [ + 'logs', + 'tmp', + 'tmp/cache', + 'tmp/cache/models', + 'tmp/cache/persistent', + 'tmp/cache/views', + 'tmp/sessions', + 'tmp/tests' + ]; + + foreach ($paths as $path) { + $path = $dir . '/' . $path; + if (!file_exists($path)) { + mkdir($path); + $io->write('Created `' . $path . '` directory'); + } + } + } + + /** + * Set globally writable permissions on the "tmp" and "logs" directory. + * + * This is not the most secure default, but it gets people up and running quickly. + * + * @param string $dir The application's root directory. + * @param \Composer\IO\IOInterface $io IO interface to write to console. + * @return void + */ + public static function setFolderPermissions($dir, $io) + { + // Change the permissions on a path and output the results. + $changePerms = function ($path, $perms, $io) { + // Get permission bits from stat(2) result. + $currentPerms = fileperms($path) & 0777; + if (($currentPerms & $perms) == $perms) { + return; + } + + $res = chmod($path, $currentPerms | $perms); + if ($res) { + $io->write('Permissions set on ' . $path); + } else { + $io->write('Failed to set permissions on ' . $path); + } + }; + + $walker = function ($dir, $perms, $io) use (&$walker, $changePerms) { + $files = array_diff(scandir($dir), ['.', '..']); + foreach ($files as $file) { + $path = $dir . '/' . $file; + + if (!is_dir($path)) { + continue; + } + + $changePerms($path, $perms, $io); + $walker($path, $perms, $io); + } + }; + + $worldWritable = bindec('0000000111'); + $walker($dir . '/tmp', $worldWritable, $io); + $changePerms($dir . '/tmp', $worldWritable, $io); + $changePerms($dir . '/logs', $worldWritable, $io); + } + + /** + * Set the security.salt value in the application's config file. + * + * @param string $dir The application's root directory. + * @param \Composer\IO\IOInterface $io IO interface to write to console. + * @return void + */ + public static function setSecuritySalt($dir, $io) + { + $config = $dir . '/config/app.php'; + $content = file_get_contents($config); + + $newKey = hash('sha256', $dir . php_uname() . microtime(true)); + $content = str_replace('__SALT__', $newKey, $content, $count); + + if ($count == 0) { + $io->write('No Security.salt placeholder to replace.'); + return; + } + + $result = file_put_contents($config, $content); + if ($result) { + $io->write('Updated Security.salt value in config/app.php'); + return; + } + $io->write('Unable to update Security.salt value.'); + } +} diff --git a/cake-3.2/src/Controller/AppController.php b/cake-3.2/src/Controller/AppController.php new file mode 100644 index 000000000..ab5190742 --- /dev/null +++ b/cake-3.2/src/Controller/AppController.php @@ -0,0 +1,62 @@ +loadComponent('Security');` + * + * @return void + */ + public function initialize() + { + parent::initialize(); + + $this->loadComponent('RequestHandler'); + $this->loadComponent('Flash'); + } + + /** + * Before render callback. + * + * @param \Cake\Event\Event $event The beforeRender event. + * @return void + */ + public function beforeRender(Event $event) + { + if (!array_key_exists('_serialize', $this->viewVars) && + in_array($this->response->type(), ['application/json', 'application/xml']) + ) { + $this->set('_serialize', true); + } + } +} diff --git a/fuel-1.8-dev/public/assets/img/index.html b/cake-3.2/src/Controller/Component/empty similarity index 100% rename from fuel-1.8-dev/public/assets/img/index.html rename to cake-3.2/src/Controller/Component/empty diff --git a/cake-3.2/src/Controller/HelloController.php b/cake-3.2/src/Controller/HelloController.php new file mode 100644 index 000000000..1064f786a --- /dev/null +++ b/cake-3.2/src/Controller/HelloController.php @@ -0,0 +1,14 @@ +response->body('Hello World!'); + return $this->response; + } +} diff --git a/cake-3.2/src/Controller/PagesController.php b/cake-3.2/src/Controller/PagesController.php new file mode 100644 index 000000000..ae7e93eee --- /dev/null +++ b/cake-3.2/src/Controller/PagesController.php @@ -0,0 +1,65 @@ +redirect('/'); + } + $page = $subpage = null; + + if (!empty($path[0])) { + $page = $path[0]; + } + if (!empty($path[1])) { + $subpage = $path[1]; + } + $this->set(compact('page', 'subpage')); + + try { + $this->render(implode('/', $path)); + } catch (MissingTemplateException $e) { + if (Configure::read('debug')) { + throw $e; + } + throw new NotFoundException(); + } + } +} diff --git a/fuel-1.8-dev/public/assets/js/index.html b/cake-3.2/src/Model/Behavior/empty similarity index 100% rename from fuel-1.8-dev/public/assets/js/index.html rename to cake-3.2/src/Model/Behavior/empty diff --git a/fuel-2.0-dev/components/demo/config/.gitkeep b/cake-3.2/src/Model/Entity/empty similarity index 100% rename from fuel-2.0-dev/components/demo/config/.gitkeep rename to cake-3.2/src/Model/Entity/empty diff --git a/fuel-2.0-dev/components/demo/lang/.gitkeep b/cake-3.2/src/Model/Table/empty similarity index 100% rename from fuel-2.0-dev/components/demo/lang/.gitkeep rename to cake-3.2/src/Model/Table/empty diff --git a/cake-3.2/src/Shell/ConsoleShell.php b/cake-3.2/src/Shell/ConsoleShell.php new file mode 100644 index 000000000..2b389068b --- /dev/null +++ b/cake-3.2/src/Shell/ConsoleShell.php @@ -0,0 +1,79 @@ +err('Unable to load Psy\Shell.'); + $this->err(''); + $this->err('Make sure you have installed psysh as a dependency,'); + $this->err('and that Psy\Shell is registered in your autoloader.'); + $this->err(''); + $this->err('If you are using composer run'); + $this->err(''); + $this->err('$ php composer.phar require --dev psy/psysh'); + $this->err(''); + return self::CODE_ERROR; + } + + $this->out("You can exit with `CTRL-C` or `exit`"); + $this->out(''); + + Log::drop('debug'); + Log::drop('error'); + $this->_io->setLoggers(false); + restore_error_handler(); + restore_exception_handler(); + + $psy = new PsyShell(); + $psy->run(); + } + + /** + * Display help for this console. + * + * @return \Cake\Console\ConsoleOptionParser + */ + public function getOptionParser() + { + $parser = new ConsoleOptionParser('console'); + $parser->description( + 'This shell provides a REPL that you can use to interact ' . + 'with your application in an interactive fashion. You can use ' . + 'it to run adhoc queries with your models, or experiment ' . + 'and explore the features of CakePHP and your application.' . + "\n\n" . + 'You will need to have psysh installed for this Shell to work.' + ); + return $parser; + } +} diff --git a/cake-3.2/src/Template/Element/Flash/default.ctp b/cake-3.2/src/Template/Element/Flash/default.ctp new file mode 100644 index 000000000..bc1e2c369 --- /dev/null +++ b/cake-3.2/src/Template/Element/Flash/default.ctp @@ -0,0 +1,7 @@ + +
diff --git a/cake-3.2/src/Template/Element/Flash/error.ctp b/cake-3.2/src/Template/Element/Flash/error.ctp new file mode 100644 index 000000000..e0f138689 --- /dev/null +++ b/cake-3.2/src/Template/Element/Flash/error.ctp @@ -0,0 +1 @@ +
diff --git a/cake-3.2/src/Template/Element/Flash/success.ctp b/cake-3.2/src/Template/Element/Flash/success.ctp new file mode 100644 index 000000000..b9c212b75 --- /dev/null +++ b/cake-3.2/src/Template/Element/Flash/success.ctp @@ -0,0 +1 @@ +
diff --git a/cake-3.2/src/Template/Email/html/default.ctp b/cake-3.2/src/Template/Email/html/default.ctp new file mode 100644 index 000000000..386674a1a --- /dev/null +++ b/cake-3.2/src/Template/Email/html/default.ctp @@ -0,0 +1,22 @@ + + ' . $line . "

\n"; +endforeach; +?> diff --git a/cake-3.2/src/Template/Email/text/default.ctp b/cake-3.2/src/Template/Email/text/default.ctp new file mode 100644 index 000000000..704b46f03 --- /dev/null +++ b/cake-3.2/src/Template/Email/text/default.ctp @@ -0,0 +1,16 @@ + + diff --git a/cake-3.2/src/Template/Error/error400.ctp b/cake-3.2/src/Template/Error/error400.ctp new file mode 100644 index 000000000..d37cc59be --- /dev/null +++ b/cake-3.2/src/Template/Error/error400.ctp @@ -0,0 +1,41 @@ +layout = 'error'; + +if (Configure::read('debug')): + $this->layout = 'dev_error'; + + $this->assign('title', $message); + $this->assign('templateName', 'error400.ctp'); + + $this->start('file'); +?> +queryString)) : ?> +

+ SQL Query: + queryString) ?> +

+ +params)) : ?> + SQL Query Params: + params) ?> + +element('auto_table_warning') ?> +end(); +endif; +?> +

+

+ : + '{$url}'" + ) ?> +

diff --git a/cake-3.2/src/Template/Error/error500.ctp b/cake-3.2/src/Template/Error/error500.ctp new file mode 100644 index 000000000..d8014f84a --- /dev/null +++ b/cake-3.2/src/Template/Error/error500.ctp @@ -0,0 +1,43 @@ +layout = 'error'; + +if (Configure::read('debug')): + $this->layout = 'dev_error'; + + $this->assign('title', $message); + $this->assign('templateName', 'error500.ctp'); + + $this->start('file'); +?> +queryString)) : ?> +

+ SQL Query: + queryString) ?> +

+ +params)) : ?> + SQL Query Params: + params) ?> + + + Error in: + getFile()), $error->getLine()) ?> + +element('auto_table_warning'); + + if (extension_loaded('xdebug')): + xdebug_print_function_stack(); + endif; + + $this->end(); +endif; +?> +

+

+ : + +

diff --git a/cake-3.2/src/Template/Layout/Email/html/default.ctp b/cake-3.2/src/Template/Layout/Email/html/default.ctp new file mode 100644 index 000000000..2b4397008 --- /dev/null +++ b/cake-3.2/src/Template/Layout/Email/html/default.ctp @@ -0,0 +1,24 @@ + + + + + <?= $this->fetch('title') ?> + + + fetch('content') ?> + + diff --git a/cake-3.2/src/Template/Layout/Email/text/default.ctp b/cake-3.2/src/Template/Layout/Email/text/default.ctp new file mode 100644 index 000000000..871dcfb48 --- /dev/null +++ b/cake-3.2/src/Template/Layout/Email/text/default.ctp @@ -0,0 +1,16 @@ + +fetch('content') ?> diff --git a/cake-3.2/src/Template/Layout/ajax.ctp b/cake-3.2/src/Template/Layout/ajax.ctp new file mode 100644 index 000000000..871dcfb48 --- /dev/null +++ b/cake-3.2/src/Template/Layout/ajax.ctp @@ -0,0 +1,16 @@ + +fetch('content') ?> diff --git a/cake-3.2/src/Template/Layout/default.ctp b/cake-3.2/src/Template/Layout/default.ctp new file mode 100644 index 000000000..8988302a5 --- /dev/null +++ b/cake-3.2/src/Template/Layout/default.ctp @@ -0,0 +1,57 @@ + + + + + Html->charset() ?> + + + <?= $cakeDescription ?>: + <?= $this->fetch('title') ?> + + Html->meta('icon') ?> + + Html->css('base.css') ?> + Html->css('cake.css') ?> + + fetch('meta') ?> + fetch('css') ?> + fetch('script') ?> + + + + Flash->render() ?> +
+ fetch('content') ?> +
+
+
+ + diff --git a/cake-3.2/src/Template/Layout/error.ctp b/cake-3.2/src/Template/Layout/error.ctp new file mode 100644 index 000000000..e6945a8ad --- /dev/null +++ b/cake-3.2/src/Template/Layout/error.ctp @@ -0,0 +1,55 @@ + + + + + Html->charset() ?> + + <?= $cakeDescription ?>: + <?= $this->fetch('title') ?> + + Html->meta('icon') ?> + + Html->css('base.css') ?> + Html->css('cake.css') ?> + + fetch('meta') ?> + fetch('css') ?> + fetch('script') ?> + + +
+ +
+ Flash->render() ?> + + fetch('content') ?> +
+ +
+ + diff --git a/cake-3.2/src/Template/Layout/rss/default.ctp b/cake-3.2/src/Template/Layout/rss/default.ctp new file mode 100644 index 000000000..5c15a198d --- /dev/null +++ b/cake-3.2/src/Template/Layout/rss/default.ctp @@ -0,0 +1,14 @@ +fetch('title'); +endif; + +echo $this->Rss->document( + $this->Rss->channel( + [], $channel, $this->fetch('content') + ) +); +?> diff --git a/cake-3.2/src/Template/Pages/home.ctp b/cake-3.2/src/Template/Pages/home.ctp new file mode 100644 index 000000000..2e4b34234 --- /dev/null +++ b/cake-3.2/src/Template/Pages/home.ctp @@ -0,0 +1,231 @@ +layout = false; + +if (!Configure::read('debug')): + throw new NotFoundException('Please replace Pages/home.ctp with your own version.'); +endif; + +$cakeDescription = 'CakePHP: the rapid development PHP framework'; +?> + + + + Html->charset() ?> + + + <?= $cakeDescription ?> + + Html->meta('icon') ?> + Html->css('base.css') ?> + Html->css('cake.css') ?> + + +
+
+ Html->image('http://cakephp.org/img/cake-logo.png') ?> +

Get the Ovens Ready

+
+
+
+
+
+ Please be aware that this page will not be shown if you turn off debug mode unless you disable the NotFoundException in src/Template/Pages/home.ctp. +
+ +
+

URL rewriting is not properly configured on your server.

+

+ 1) Help me configure it +

+

+ 2) I don't / can't use URL rewriting +

+
+ +
+

Environment

+ =')): ?> +

Your version of PHP is 5.5.9 or higher.

+ +

Your version of PHP is too low. You need PHP 5.5.9 or higher to use CakePHP.

+ + + +

Your version of PHP has the mbstring extension loaded.

+ +

Your version of PHP does NOT have the mbstring extension loaded.

; + + + +

Your version of PHP has the openssl extension loaded.

+ +

Your version of PHP has the mcrypt extension loaded.

+ +

Your version of PHP does NOT have the openssl or mcrypt extension loaded.

+ + + +

Your version of PHP has the intl extension loaded.

+ +

Your version of PHP does NOT have the intl extension loaded.

+ +
+ +

Filesystem

+ +

Your tmp directory is writable.

+ +

Your tmp directory is NOT writable.

+ + + +

Your logs directory is writable.

+ +

Your logs directory is NOT writable.

+ + + + +

The Engine is being used for core caching. To change the config edit config/app.php

+ +

Your cache is NOT working. Please check the settings in config/app.php

+ + +
+

Database

+ connect(); + } catch (Exception $connectionError) { + $connected = false; + $errorMsg = $connectionError->getMessage(); + if (method_exists($connectionError, 'getAttributes')): + $attributes = $connectionError->getAttributes(); + if (isset($errorMsg['message'])): + $errorMsg .= '
' . $attributes['message']; + endif; + endif; + } + ?> + +

CakePHP is able to connect to the database.

+ +

CakePHP is NOT able to connect to the database.

+ +
+
+ +
+
+

Editing this Page

+
    +
  • To change the content of this page, edit: src/Template/Pages/home.ctp.
  • +
  • You can also add some CSS styles for your pages at: webroot/css/.
  • +
+
+ +
+
+ +
+
+

More about Cake

+

+ CakePHP is a rapid development framework for PHP which uses commonly known design patterns like Front Controller and MVC. +

+

+ Our primary goal is to provide a structured framework that enables PHP users at all levels to rapidly develop robust web applications, without any loss to flexibility. +

+ +

Help and Bug Reports

+ + +

Docs and Downloads

+ + +

Training and Certification

+ +
+
+
+ + diff --git a/cake-3.2/src/View/AjaxView.php b/cake-3.2/src/View/AjaxView.php new file mode 100644 index 000000000..594e2d805 --- /dev/null +++ b/cake-3.2/src/View/AjaxView.php @@ -0,0 +1,49 @@ +response->type('ajax'); + } +} diff --git a/cake-3.2/src/View/AppView.php b/cake-3.2/src/View/AppView.php new file mode 100644 index 000000000..fd52ba695 --- /dev/null +++ b/cake-3.2/src/View/AppView.php @@ -0,0 +1,40 @@ +loadHelper('Html');` + * + * @return void + */ + public function initialize() + { + } +} diff --git a/fuel-2.0-dev/components/demo/tests/.gitkeep b/cake-3.2/src/View/Helper/empty similarity index 100% rename from fuel-2.0-dev/components/demo/tests/.gitkeep rename to cake-3.2/src/View/Helper/empty diff --git a/fuel-2.0-dev/public/assets/css/index.html b/cake-3.2/tests/Fixture/empty similarity index 100% rename from fuel-2.0-dev/public/assets/css/index.html rename to cake-3.2/tests/Fixture/empty diff --git a/fuel-2.0-dev/public/assets/fonts/index.html b/cake-3.2/tests/TestCase/Controller/Component/empty similarity index 100% rename from fuel-2.0-dev/public/assets/fonts/index.html rename to cake-3.2/tests/TestCase/Controller/Component/empty diff --git a/cake-3.2/tests/TestCase/Controller/PagesControllerTest.php b/cake-3.2/tests/TestCase/Controller/PagesControllerTest.php new file mode 100644 index 000000000..225a8ad4d --- /dev/null +++ b/cake-3.2/tests/TestCase/Controller/PagesControllerTest.php @@ -0,0 +1,73 @@ +get('/pages/home'); + $this->assertResponseOk(); + $this->assertResponseContains('CakePHP'); + $this->assertResponseContains(''); + } + + /** + * Test that missing template renders 404 page in production + * + * @return void + */ + public function testMissingTemplate() + { + Configure::write('debug', false); + $this->get('/pages/not_existing'); + + $this->assertResponseError(); + $this->assertResponseContains('Error'); + } + + /** + * Test that missing template in debug mode renders missing_template error page + * + * @return void + */ + public function testMissingTemplateInDebug() + { + Configure::write('debug', true); + $this->get('/pages/not_existing'); + + $this->assertResponseFailure(); + $this->assertResponseContains('Missing Template'); + $this->assertResponseContains('Stacktrace'); + $this->assertResponseContains('not_existing.ctp'); + } +} diff --git a/fuel-2.0-dev/public/assets/img/index.html b/cake-3.2/tests/TestCase/Model/Behavior/empty similarity index 100% rename from fuel-2.0-dev/public/assets/img/index.html rename to cake-3.2/tests/TestCase/Model/Behavior/empty diff --git a/fuel-2.0-dev/public/assets/js/index.html b/cake-3.2/tests/TestCase/View/Helper/empty similarity index 100% rename from fuel-2.0-dev/public/assets/js/index.html rename to cake-3.2/tests/TestCase/View/Helper/empty diff --git a/cake-3.2/tests/bootstrap.php b/cake-3.2/tests/bootstrap.php new file mode 100644 index 000000000..e56aa9df2 --- /dev/null +++ b/cake-3.2/tests/bootstrap.php @@ -0,0 +1,8 @@ +.column,.row.collapse>.columns{padding-left:0;padding-right:0}.row.collapse .row{margin-left:0;margin-right:0}.row .row{margin:0 -0.9375rem;max-width:none;width:auto}.row .row:before,.row .row:after{content:" ";display:table}.row .row:after{clear:both}.row .row.collapse{margin:0;max-width:none;width:auto}.row .row.collapse:before,.row .row.collapse:after{content:" ";display:table}.row .row.collapse:after{clear:both}.column,.columns{padding-left:0.9375rem;padding-right:0.9375rem;width:100%;float:left}.column+.column:last-child,.columns+.column:last-child,.column+.columns:last-child,.columns+.columns:last-child{float:right}.column+.column.end,.columns+.column.end,.column+.columns.end,.columns+.columns.end{float:left}@media only screen{.small-push-0{position:relative;left:0;right:auto}.small-pull-0{position:relative;right:0;left:auto}.small-push-1{position:relative;left:8.33333%;right:auto}.small-pull-1{position:relative;right:8.33333%;left:auto}.small-push-2{position:relative;left:16.66667%;right:auto}.small-pull-2{position:relative;right:16.66667%;left:auto}.small-push-3{position:relative;left:25%;right:auto}.small-pull-3{position:relative;right:25%;left:auto}.small-push-4{position:relative;left:33.33333%;right:auto}.small-pull-4{position:relative;right:33.33333%;left:auto}.small-push-5{position:relative;left:41.66667%;right:auto}.small-pull-5{position:relative;right:41.66667%;left:auto}.small-push-6{position:relative;left:50%;right:auto}.small-pull-6{position:relative;right:50%;left:auto}.small-push-7{position:relative;left:58.33333%;right:auto}.small-pull-7{position:relative;right:58.33333%;left:auto}.small-push-8{position:relative;left:66.66667%;right:auto}.small-pull-8{position:relative;right:66.66667%;left:auto}.small-push-9{position:relative;left:75%;right:auto}.small-pull-9{position:relative;right:75%;left:auto}.small-push-10{position:relative;left:83.33333%;right:auto}.small-pull-10{position:relative;right:83.33333%;left:auto}.small-push-11{position:relative;left:91.66667%;right:auto}.small-pull-11{position:relative;right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375rem;padding-right:0.9375rem;float:left}.small-1{width:8.33333%}.small-2{width:16.66667%}.small-3{width:25%}.small-4{width:33.33333%}.small-5{width:41.66667%}.small-6{width:50%}.small-7{width:58.33333%}.small-8{width:66.66667%}.small-9{width:75%}.small-10{width:83.33333%}.small-11{width:91.66667%}.small-12{width:100%}.small-offset-0{margin-left:0 !important}.small-offset-1{margin-left:8.33333% !important}.small-offset-2{margin-left:16.66667% !important}.small-offset-3{margin-left:25% !important}.small-offset-4{margin-left:33.33333% !important}.small-offset-5{margin-left:41.66667% !important}.small-offset-6{margin-left:50% !important}.small-offset-7{margin-left:58.33333% !important}.small-offset-8{margin-left:66.66667% !important}.small-offset-9{margin-left:75% !important}.small-offset-10{margin-left:83.33333% !important}.small-offset-11{margin-left:91.66667% !important}.small-reset-order{float:left;left:auto;margin-left:0;margin-right:0;right:auto}.column.small-centered,.columns.small-centered{margin-left:auto;margin-right:auto;float:none}.column.small-uncentered,.columns.small-uncentered{float:left;margin-left:0;margin-right:0}.column.small-centered:last-child,.columns.small-centered:last-child{float:none}.column.small-uncentered:last-child,.columns.small-uncentered:last-child{float:left}.column.small-uncentered.opposite,.columns.small-uncentered.opposite{float:right}.row.small-collapse>.column,.row.small-collapse>.columns{padding-left:0;padding-right:0}.row.small-collapse .row{margin-left:0;margin-right:0}.row.small-uncollapse>.column,.row.small-uncollapse>.columns{padding-left:0.9375rem;padding-right:0.9375rem;float:left}}@media only screen and (min-width: 40.0625em){.medium-push-0{position:relative;left:0;right:auto}.medium-pull-0{position:relative;right:0;left:auto}.medium-push-1{position:relative;left:8.33333%;right:auto}.medium-pull-1{position:relative;right:8.33333%;left:auto}.medium-push-2{position:relative;left:16.66667%;right:auto}.medium-pull-2{position:relative;right:16.66667%;left:auto}.medium-push-3{position:relative;left:25%;right:auto}.medium-pull-3{position:relative;right:25%;left:auto}.medium-push-4{position:relative;left:33.33333%;right:auto}.medium-pull-4{position:relative;right:33.33333%;left:auto}.medium-push-5{position:relative;left:41.66667%;right:auto}.medium-pull-5{position:relative;right:41.66667%;left:auto}.medium-push-6{position:relative;left:50%;right:auto}.medium-pull-6{position:relative;right:50%;left:auto}.medium-push-7{position:relative;left:58.33333%;right:auto}.medium-pull-7{position:relative;right:58.33333%;left:auto}.medium-push-8{position:relative;left:66.66667%;right:auto}.medium-pull-8{position:relative;right:66.66667%;left:auto}.medium-push-9{position:relative;left:75%;right:auto}.medium-pull-9{position:relative;right:75%;left:auto}.medium-push-10{position:relative;left:83.33333%;right:auto}.medium-pull-10{position:relative;right:83.33333%;left:auto}.medium-push-11{position:relative;left:91.66667%;right:auto}.medium-pull-11{position:relative;right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375rem;padding-right:0.9375rem;float:left}.medium-1{width:8.33333%}.medium-2{width:16.66667%}.medium-3{width:25%}.medium-4{width:33.33333%}.medium-5{width:41.66667%}.medium-6{width:50%}.medium-7{width:58.33333%}.medium-8{width:66.66667%}.medium-9{width:75%}.medium-10{width:83.33333%}.medium-11{width:91.66667%}.medium-12{width:100%}.medium-offset-0{margin-left:0 !important}.medium-offset-1{margin-left:8.33333% !important}.medium-offset-2{margin-left:16.66667% !important}.medium-offset-3{margin-left:25% !important}.medium-offset-4{margin-left:33.33333% !important}.medium-offset-5{margin-left:41.66667% !important}.medium-offset-6{margin-left:50% !important}.medium-offset-7{margin-left:58.33333% !important}.medium-offset-8{margin-left:66.66667% !important}.medium-offset-9{margin-left:75% !important}.medium-offset-10{margin-left:83.33333% !important}.medium-offset-11{margin-left:91.66667% !important}.medium-reset-order{float:left;left:auto;margin-left:0;margin-right:0;right:auto}.column.medium-centered,.columns.medium-centered{margin-left:auto;margin-right:auto;float:none}.column.medium-uncentered,.columns.medium-uncentered{float:left;margin-left:0;margin-right:0}.column.medium-centered:last-child,.columns.medium-centered:last-child{float:none}.column.medium-uncentered:last-child,.columns.medium-uncentered:last-child{float:left}.column.medium-uncentered.opposite,.columns.medium-uncentered.opposite{float:right}.row.medium-collapse>.column,.row.medium-collapse>.columns{padding-left:0;padding-right:0}.row.medium-collapse .row{margin-left:0;margin-right:0}.row.medium-uncollapse>.column,.row.medium-uncollapse>.columns{padding-left:0.9375rem;padding-right:0.9375rem;float:left}.push-0{position:relative;left:0;right:auto}.pull-0{position:relative;right:0;left:auto}.push-1{position:relative;left:8.33333%;right:auto}.pull-1{position:relative;right:8.33333%;left:auto}.push-2{position:relative;left:16.66667%;right:auto}.pull-2{position:relative;right:16.66667%;left:auto}.push-3{position:relative;left:25%;right:auto}.pull-3{position:relative;right:25%;left:auto}.push-4{position:relative;left:33.33333%;right:auto}.pull-4{position:relative;right:33.33333%;left:auto}.push-5{position:relative;left:41.66667%;right:auto}.pull-5{position:relative;right:41.66667%;left:auto}.push-6{position:relative;left:50%;right:auto}.pull-6{position:relative;right:50%;left:auto}.push-7{position:relative;left:58.33333%;right:auto}.pull-7{position:relative;right:58.33333%;left:auto}.push-8{position:relative;left:66.66667%;right:auto}.pull-8{position:relative;right:66.66667%;left:auto}.push-9{position:relative;left:75%;right:auto}.pull-9{position:relative;right:75%;left:auto}.push-10{position:relative;left:83.33333%;right:auto}.pull-10{position:relative;right:83.33333%;left:auto}.push-11{position:relative;left:91.66667%;right:auto}.pull-11{position:relative;right:91.66667%;left:auto}}@media only screen and (min-width: 64.0625em){.large-push-0{position:relative;left:0;right:auto}.large-pull-0{position:relative;right:0;left:auto}.large-push-1{position:relative;left:8.33333%;right:auto}.large-pull-1{position:relative;right:8.33333%;left:auto}.large-push-2{position:relative;left:16.66667%;right:auto}.large-pull-2{position:relative;right:16.66667%;left:auto}.large-push-3{position:relative;left:25%;right:auto}.large-pull-3{position:relative;right:25%;left:auto}.large-push-4{position:relative;left:33.33333%;right:auto}.large-pull-4{position:relative;right:33.33333%;left:auto}.large-push-5{position:relative;left:41.66667%;right:auto}.large-pull-5{position:relative;right:41.66667%;left:auto}.large-push-6{position:relative;left:50%;right:auto}.large-pull-6{position:relative;right:50%;left:auto}.large-push-7{position:relative;left:58.33333%;right:auto}.large-pull-7{position:relative;right:58.33333%;left:auto}.large-push-8{position:relative;left:66.66667%;right:auto}.large-pull-8{position:relative;right:66.66667%;left:auto}.large-push-9{position:relative;left:75%;right:auto}.large-pull-9{position:relative;right:75%;left:auto}.large-push-10{position:relative;left:83.33333%;right:auto}.large-pull-10{position:relative;right:83.33333%;left:auto}.large-push-11{position:relative;left:91.66667%;right:auto}.large-pull-11{position:relative;right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375rem;padding-right:0.9375rem;float:left}.large-1{width:8.33333%}.large-2{width:16.66667%}.large-3{width:25%}.large-4{width:33.33333%}.large-5{width:41.66667%}.large-6{width:50%}.large-7{width:58.33333%}.large-8{width:66.66667%}.large-9{width:75%}.large-10{width:83.33333%}.large-11{width:91.66667%}.large-12{width:100%}.large-offset-0{margin-left:0 !important}.large-offset-1{margin-left:8.33333% !important}.large-offset-2{margin-left:16.66667% !important}.large-offset-3{margin-left:25% !important}.large-offset-4{margin-left:33.33333% !important}.large-offset-5{margin-left:41.66667% !important}.large-offset-6{margin-left:50% !important}.large-offset-7{margin-left:58.33333% !important}.large-offset-8{margin-left:66.66667% !important}.large-offset-9{margin-left:75% !important}.large-offset-10{margin-left:83.33333% !important}.large-offset-11{margin-left:91.66667% !important}.large-reset-order{float:left;left:auto;margin-left:0;margin-right:0;right:auto}.column.large-centered,.columns.large-centered{margin-left:auto;margin-right:auto;float:none}.column.large-uncentered,.columns.large-uncentered{float:left;margin-left:0;margin-right:0}.column.large-centered:last-child,.columns.large-centered:last-child{float:none}.column.large-uncentered:last-child,.columns.large-uncentered:last-child{float:left}.column.large-uncentered.opposite,.columns.large-uncentered.opposite{float:right}.row.large-collapse>.column,.row.large-collapse>.columns{padding-left:0;padding-right:0}.row.large-collapse .row{margin-left:0;margin-right:0}.row.large-uncollapse>.column,.row.large-uncollapse>.columns{padding-left:0.9375rem;padding-right:0.9375rem;float:left}.push-0{position:relative;left:0;right:auto}.pull-0{position:relative;right:0;left:auto}.push-1{position:relative;left:8.33333%;right:auto}.pull-1{position:relative;right:8.33333%;left:auto}.push-2{position:relative;left:16.66667%;right:auto}.pull-2{position:relative;right:16.66667%;left:auto}.push-3{position:relative;left:25%;right:auto}.pull-3{position:relative;right:25%;left:auto}.push-4{position:relative;left:33.33333%;right:auto}.pull-4{position:relative;right:33.33333%;left:auto}.push-5{position:relative;left:41.66667%;right:auto}.pull-5{position:relative;right:41.66667%;left:auto}.push-6{position:relative;left:50%;right:auto}.pull-6{position:relative;right:50%;left:auto}.push-7{position:relative;left:58.33333%;right:auto}.pull-7{position:relative;right:58.33333%;left:auto}.push-8{position:relative;left:66.66667%;right:auto}.pull-8{position:relative;right:66.66667%;left:auto}.push-9{position:relative;left:75%;right:auto}.pull-9{position:relative;right:75%;left:auto}.push-10{position:relative;left:83.33333%;right:auto}.pull-10{position:relative;right:83.33333%;left:auto}.push-11{position:relative;left:91.66667%;right:auto}.pull-11{position:relative;right:91.66667%;left:auto}}button,.button{-webkit-appearance:none;-moz-appearance:none;border-radius:0;border-style:solid;border-width:0;cursor:pointer;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-weight:normal;line-height:normal;margin:0 0 1.25rem;position:relative;text-align:center;text-decoration:none;display:inline-block;padding:1rem 2rem 1.0625rem 2rem;font-size:1rem;background-color:#008CBA;border-color:#007095;color:#fff;transition:background-color 300ms ease-out}button:hover,button:focus,.button:hover,.button:focus{background-color:#007095}button:hover,button:focus,.button:hover,.button:focus{color:#fff}button.secondary,.button.secondary{background-color:#e7e7e7;border-color:#b9b9b9;color:#333}button.secondary:hover,button.secondary:focus,.button.secondary:hover,.button.secondary:focus{background-color:#b9b9b9}button.secondary:hover,button.secondary:focus,.button.secondary:hover,.button.secondary:focus{color:#333}button.success,.button.success{background-color:#43AC6A;border-color:#368a55;color:#fff}button.success:hover,button.success:focus,.button.success:hover,.button.success:focus{background-color:#368a55}button.success:hover,button.success:focus,.button.success:hover,.button.success:focus{color:#fff}button.alert,.button.alert{background-color:#f04124;border-color:#cf2a0e;color:#fff}button.alert:hover,button.alert:focus,.button.alert:hover,.button.alert:focus{background-color:#cf2a0e}button.alert:hover,button.alert:focus,.button.alert:hover,.button.alert:focus{color:#fff}button.warning,.button.warning{background-color:#f08a24;border-color:#cf6e0e;color:#fff}button.warning:hover,button.warning:focus,.button.warning:hover,.button.warning:focus{background-color:#cf6e0e}button.warning:hover,button.warning:focus,.button.warning:hover,.button.warning:focus{color:#fff}button.info,.button.info{background-color:#a0d3e8;border-color:#61b6d9;color:#333}button.info:hover,button.info:focus,.button.info:hover,.button.info:focus{background-color:#61b6d9}button.info:hover,button.info:focus,.button.info:hover,.button.info:focus{color:#fff}button.large,.button.large{padding:1.125rem 2.25rem 1.1875rem 2.25rem;font-size:1.25rem}button.small,.button.small{padding:0.875rem 1.75rem 0.9375rem 1.75rem;font-size:0.8125rem}button.tiny,.button.tiny{padding:0.625rem 1.25rem 0.6875rem 1.25rem;font-size:0.6875rem}button.expand,.button.expand{padding-left:0;padding-right:0;width:100%}button.left-align,.button.left-align{text-align:left;text-indent:0.75rem}button.right-align,.button.right-align{text-align:right;padding-right:0.75rem}button.radius,.button.radius{border-radius:3px}button.round,.button.round{border-radius:1000px}button.disabled,button[disabled],.button.disabled,.button[disabled]{background-color:#008CBA;border-color:#007095;color:#fff;box-shadow:none;cursor:default;opacity:0.7}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{background-color:#007095}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{color:#fff}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{background-color:#008CBA}button.disabled.secondary,button[disabled].secondary,.button.disabled.secondary,.button[disabled].secondary{background-color:#e7e7e7;border-color:#b9b9b9;color:#333;box-shadow:none;cursor:default;opacity:0.7}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{background-color:#b9b9b9}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{color:#333}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{background-color:#e7e7e7}button.disabled.success,button[disabled].success,.button.disabled.success,.button[disabled].success{background-color:#43AC6A;border-color:#368a55;color:#fff;box-shadow:none;cursor:default;opacity:0.7}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{background-color:#368a55}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{color:#fff}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{background-color:#43AC6A}button.disabled.alert,button[disabled].alert,.button.disabled.alert,.button[disabled].alert{background-color:#f04124;border-color:#cf2a0e;color:#fff;box-shadow:none;cursor:default;opacity:0.7}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{background-color:#cf2a0e}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{color:#fff}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{background-color:#f04124}button.disabled.warning,button[disabled].warning,.button.disabled.warning,.button[disabled].warning{background-color:#f08a24;border-color:#cf6e0e;color:#fff;box-shadow:none;cursor:default;opacity:0.7}button.disabled.warning:hover,button.disabled.warning:focus,button[disabled].warning:hover,button[disabled].warning:focus,.button.disabled.warning:hover,.button.disabled.warning:focus,.button[disabled].warning:hover,.button[disabled].warning:focus{background-color:#cf6e0e}button.disabled.warning:hover,button.disabled.warning:focus,button[disabled].warning:hover,button[disabled].warning:focus,.button.disabled.warning:hover,.button.disabled.warning:focus,.button[disabled].warning:hover,.button[disabled].warning:focus{color:#fff}button.disabled.warning:hover,button.disabled.warning:focus,button[disabled].warning:hover,button[disabled].warning:focus,.button.disabled.warning:hover,.button.disabled.warning:focus,.button[disabled].warning:hover,.button[disabled].warning:focus{background-color:#f08a24}button.disabled.info,button[disabled].info,.button.disabled.info,.button[disabled].info{background-color:#a0d3e8;border-color:#61b6d9;color:#333;box-shadow:none;cursor:default;opacity:0.7}button.disabled.info:hover,button.disabled.info:focus,button[disabled].info:hover,button[disabled].info:focus,.button.disabled.info:hover,.button.disabled.info:focus,.button[disabled].info:hover,.button[disabled].info:focus{background-color:#61b6d9}button.disabled.info:hover,button.disabled.info:focus,button[disabled].info:hover,button[disabled].info:focus,.button.disabled.info:hover,.button.disabled.info:focus,.button[disabled].info:hover,.button[disabled].info:focus{color:#fff}button.disabled.info:hover,button.disabled.info:focus,button[disabled].info:hover,button[disabled].info:focus,.button.disabled.info:hover,.button.disabled.info:focus,.button[disabled].info:hover,.button[disabled].info:focus{background-color:#a0d3e8}button::-moz-focus-inner{border:0;padding:0}@media only screen and (min-width: 40.0625em){button,.button{display:inline-block}}form{margin:0 0 1rem}form .row .row{margin:0 -0.5rem}form .row .row .column,form .row .row .columns{padding:0 0.5rem}form .row .row.collapse{margin:0}form .row .row.collapse .column,form .row .row.collapse .columns{padding:0}form .row .row.collapse input{-webkit-border-bottom-right-radius:0;-webkit-border-top-right-radius:0;border-bottom-right-radius:0;border-top-right-radius:0}form .row input.column,form .row input.columns,form .row textarea.column,form .row textarea.columns{padding-left:0.5rem}label{color:#4d4d4d;cursor:pointer;display:block;font-size:0.875rem;font-weight:normal;line-height:1.5;margin-bottom:0}label.right{float:none !important;text-align:right}label.inline{margin:0 0 1rem 0;padding:0.5625rem 0}label small{text-transform:capitalize;color:#676767}.prefix,.postfix{border-style:solid;border-width:1px;display:block;font-size:0.875rem;height:2.3125rem;line-height:2.3125rem;overflow:visible;padding-bottom:0;padding-top:0;position:relative;text-align:center;width:100%;z-index:2}.postfix.button{border-color:true}.prefix.button{border:none;padding-left:0;padding-right:0;padding-bottom:0;padding-top:0;text-align:center}.prefix.button.radius{border-radius:0;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.postfix.button.radius{border-radius:0;-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.prefix.button.round{border-radius:0;-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}.postfix.button.round{border-radius:0;-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}span.prefix,label.prefix{background:#f2f2f2;border-right:none;color:#333;border-color:#ccc}span.postfix,label.postfix{background:#f2f2f2;color:#333;border-color:#ccc}input[type="text"],input[type="password"],input[type="date"],input[type="datetime"],input[type="datetime-local"],input[type="month"],input[type="week"],input[type="email"],input[type="number"],input[type="search"],input[type="tel"],input[type="time"],input[type="url"],input[type="color"],textarea{-webkit-appearance:none;-moz-appearance:none;border-radius:0;background-color:#fff;border-style:solid;border-width:1px;border-color:#ccc;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);color:rgba(0,0,0,0.75);display:block;font-family:inherit;font-size:0.875rem;height:2.3125rem;margin:0 0 1rem 0;padding:0.5rem;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:border-color 0.15s linear,background 0.15s linear;-moz-transition:border-color 0.15s linear,background 0.15s linear;-ms-transition:border-color 0.15s linear,background 0.15s linear;-o-transition:border-color 0.15s linear,background 0.15s linear;transition:border-color 0.15s linear,background 0.15s linear}input[type="text"]:focus,input[type="password"]:focus,input[type="date"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="month"]:focus,input[type="week"]:focus,input[type="email"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="time"]:focus,input[type="url"]:focus,input[type="color"]:focus,textarea:focus{background:#fafafa;border-color:#999;outline:none}input[type="text"]:disabled,input[type="password"]:disabled,input[type="date"]:disabled,input[type="datetime"]:disabled,input[type="datetime-local"]:disabled,input[type="month"]:disabled,input[type="week"]:disabled,input[type="email"]:disabled,input[type="number"]:disabled,input[type="search"]:disabled,input[type="tel"]:disabled,input[type="time"]:disabled,input[type="url"]:disabled,input[type="color"]:disabled,textarea:disabled{background-color:#ddd;cursor:default}input[type="text"][disabled],input[type="text"][readonly],fieldset[disabled] input[type="text"],input[type="password"][disabled],input[type="password"][readonly],fieldset[disabled] input[type="password"],input[type="date"][disabled],input[type="date"][readonly],fieldset[disabled] input[type="date"],input[type="datetime"][disabled],input[type="datetime"][readonly],fieldset[disabled] input[type="datetime"],input[type="datetime-local"][disabled],input[type="datetime-local"][readonly],fieldset[disabled] input[type="datetime-local"],input[type="month"][disabled],input[type="month"][readonly],fieldset[disabled] input[type="month"],input[type="week"][disabled],input[type="week"][readonly],fieldset[disabled] input[type="week"],input[type="email"][disabled],input[type="email"][readonly],fieldset[disabled] input[type="email"],input[type="number"][disabled],input[type="number"][readonly],fieldset[disabled] input[type="number"],input[type="search"][disabled],input[type="search"][readonly],fieldset[disabled] input[type="search"],input[type="tel"][disabled],input[type="tel"][readonly],fieldset[disabled] input[type="tel"],input[type="time"][disabled],input[type="time"][readonly],fieldset[disabled] input[type="time"],input[type="url"][disabled],input[type="url"][readonly],fieldset[disabled] input[type="url"],input[type="color"][disabled],input[type="color"][readonly],fieldset[disabled] input[type="color"],textarea[disabled],textarea[readonly],fieldset[disabled] textarea{background-color:#ddd;cursor:default}input[type="text"].radius,input[type="password"].radius,input[type="date"].radius,input[type="datetime"].radius,input[type="datetime-local"].radius,input[type="month"].radius,input[type="week"].radius,input[type="email"].radius,input[type="number"].radius,input[type="search"].radius,input[type="tel"].radius,input[type="time"].radius,input[type="url"].radius,input[type="color"].radius,textarea.radius{border-radius:3px}form .row .prefix-radius.row.collapse input,form .row .prefix-radius.row.collapse textarea,form .row .prefix-radius.row.collapse select,form .row .prefix-radius.row.collapse button{border-radius:0;-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}form .row .prefix-radius.row.collapse .prefix{border-radius:0;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}form .row .postfix-radius.row.collapse input,form .row .postfix-radius.row.collapse textarea,form .row .postfix-radius.row.collapse select,form .row .postfix-radius.row.collapse button{border-radius:0;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}form .row .postfix-radius.row.collapse .postfix{border-radius:0;-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}form .row .prefix-round.row.collapse input,form .row .prefix-round.row.collapse textarea,form .row .prefix-round.row.collapse select,form .row .prefix-round.row.collapse button{border-radius:0;-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}form .row .prefix-round.row.collapse .prefix{border-radius:0;-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}form .row .postfix-round.row.collapse input,form .row .postfix-round.row.collapse textarea,form .row .postfix-round.row.collapse select,form .row .postfix-round.row.collapse button{border-radius:0;-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}form .row .postfix-round.row.collapse .postfix{border-radius:0;-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}input[type="submit"]{-webkit-appearance:none;-moz-appearance:none;border-radius:0}textarea[rows]{height:auto}textarea{max-width:100%}::-webkit-input-placeholder{color:#ccc}:-moz-placeholder{color:#ccc}::-moz-placeholder{color:#ccc}:-ms-input-placeholder{color:#ccc}select{-webkit-appearance:none !important;-moz-appearance:none !important;background-color:#FAFAFA;border-radius:0;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMTJweCIgeT0iMHB4IiB3aWR0aD0iMjRweCIgaGVpZ2h0PSIzcHgiIHZpZXdCb3g9IjAgMCA2IDMiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDYgMyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBvbHlnb24gcG9pbnRzPSI1Ljk5MiwwIDIuOTkyLDMgLTAuMDA4LDAgIi8+PC9zdmc+);background-position:100% center;background-repeat:no-repeat;border-style:solid;border-width:1px;border-color:#ccc;color:rgba(0,0,0,0.75);font-family:inherit;font-size:0.875rem;line-height:normal;padding:0.5rem;border-radius:0;height:2.3125rem}select::-ms-expand{display:none}select.radius{border-radius:3px}select:hover{background-color:#f3f3f3;border-color:#999}select:disabled{background-color:#ddd;cursor:default}select[multiple]{height:auto}input[type="file"],input[type="checkbox"],input[type="radio"],select{margin:0 0 1rem 0}input[type="checkbox"]+label,input[type="radio"]+label{display:inline-block;margin-left:0.5rem;margin-right:1rem;margin-bottom:0;vertical-align:baseline}input[type="file"]{width:100%}fieldset{border:1px solid #ddd;margin:1.125rem 0;padding:1.25rem}fieldset legend{background:#fff;font-weight:bold;margin-left:-0.1875rem;margin:0;padding:0 0.1875rem}[data-abide] .error small.error,[data-abide] .error span.error,[data-abide] span.error,[data-abide] small.error{display:block;font-size:0.75rem;font-style:italic;font-weight:normal;margin-bottom:1rem;margin-top:-1px;padding:0.375rem 0.5625rem 0.5625rem;background:#f04124;color:#fff}[data-abide] span.error,[data-abide] small.error{display:none}span.error,small.error{display:block;font-size:0.75rem;font-style:italic;font-weight:normal;margin-bottom:1rem;margin-top:-1px;padding:0.375rem 0.5625rem 0.5625rem;background:#f04124;color:#fff}.error input,.error textarea,.error select{margin-bottom:0}.error input[type="checkbox"],.error input[type="radio"]{margin-bottom:1rem}.error label,.error label.error{color:#f04124}.error small.error{display:block;font-size:0.75rem;font-style:italic;font-weight:normal;margin-bottom:1rem;margin-top:-1px;padding:0.375rem 0.5625rem 0.5625rem;background:#f04124;color:#fff}.error>label>small{background:transparent;color:#676767;display:inline;font-size:60%;font-style:normal;margin:0;padding:0;text-transform:capitalize}.error span.error-message{display:block}input.error,textarea.error,select.error{margin-bottom:0}label.error{color:#f04124}meta.foundation-mq-topbar{font-family:"/only screen and (min-width:40.0625em)/";width:40.0625em}.contain-to-grid{width:100%;background:#333}.contain-to-grid .top-bar{margin-bottom:0}.fixed{position:fixed;top:0;width:100%;z-index:99;left:0}.fixed.expanded:not(.top-bar){height:auto;max-height:100%;overflow-y:auto;width:100%}.fixed.expanded:not(.top-bar) .title-area{position:fixed;width:100%;z-index:99}.fixed.expanded:not(.top-bar) .top-bar-section{margin-top:2.8125rem;z-index:98}.top-bar{background:#333;height:2.8125rem;line-height:2.8125rem;margin-bottom:0;overflow:hidden;position:relative}.top-bar ul{list-style:none;margin-bottom:0}.top-bar .row{max-width:none}.top-bar form,.top-bar input,.top-bar select{margin-bottom:0}.top-bar input,.top-bar select{font-size:0.75rem;height:1.75rem;padding-bottom:.35rem;padding-top:.35rem}.top-bar .button,.top-bar button{font-size:0.75rem;margin-bottom:0;padding-bottom:0.4125rem;padding-top:0.4125rem}@media only screen and (max-width: 40em){.top-bar .button,.top-bar button{position:relative;top:-1px}}.top-bar .title-area{margin:0;position:relative}.top-bar .name{font-size:16px;height:2.8125rem;margin:0}.top-bar .name h1,.top-bar .name h2,.top-bar .name h3,.top-bar .name h4,.top-bar .name p,.top-bar .name span{font-size:1.0625rem;line-height:2.8125rem;margin:0}.top-bar .name h1 a,.top-bar .name h2 a,.top-bar .name h3 a,.top-bar .name h4 a,.top-bar .name p a,.top-bar .name span a{color:#fff;display:block;font-weight:normal;padding:0 0.9375rem;width:75%}.top-bar .toggle-topbar{position:absolute;right:0;top:0}.top-bar .toggle-topbar a{color:#fff;display:block;font-size:0.8125rem;font-weight:bold;height:2.8125rem;line-height:2.8125rem;padding:0 0.9375rem;position:relative;text-transform:uppercase}.top-bar .toggle-topbar.menu-icon{margin-top:-16px;top:50%}.top-bar .toggle-topbar.menu-icon a{color:#fff;height:34px;line-height:33px;padding:0 2.5rem 0 0.9375rem;position:relative}.top-bar .toggle-topbar.menu-icon a span::after{content:"";display:block;height:0;position:absolute;margin-top:-8px;top:50%;right:0.9375rem;box-shadow:0 0 0 1px #fff,0 7px 0 1px #fff,0 14px 0 1px #fff;width:16px}.top-bar .toggle-topbar.menu-icon a span:hover:after{box-shadow:0 0 0 1px "",0 7px 0 1px "",0 14px 0 1px ""}.top-bar.expanded{background:transparent;height:auto}.top-bar.expanded .title-area{background:#333}.top-bar.expanded .toggle-topbar a{color:#888}.top-bar.expanded .toggle-topbar a span::after{box-shadow:0 0 0 1px #888,0 7px 0 1px #888,0 14px 0 1px #888}@media screen and (-webkit-min-device-pixel-ratio: 0){.top-bar.expanded .top-bar-section .has-dropdown.moved>.dropdown,.top-bar.expanded .top-bar-section .dropdown{clip:initial}.top-bar.expanded .top-bar-section .has-dropdown:not(.moved)>ul{padding:0}}.top-bar-section{left:0;position:relative;width:auto;transition:left 300ms ease-out}.top-bar-section ul{display:block;font-size:16px;height:auto;margin:0;padding:0;width:100%}.top-bar-section .divider,.top-bar-section [role="separator"]{border-top:solid 1px #1a1a1a;clear:both;height:1px;width:100%}.top-bar-section ul li{background:#333}.top-bar-section ul li>a{color:#fff;display:block;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:0.8125rem;font-weight:normal;padding-left:0.9375rem;padding:12px 0 12px 0.9375rem;text-transform:none;width:100%}.top-bar-section ul li>a.button{font-size:0.8125rem;padding-left:0.9375rem;padding-right:0.9375rem;background-color:#008CBA;border-color:#007095;color:#fff}.top-bar-section ul li>a.button:hover,.top-bar-section ul li>a.button:focus{background-color:#007095}.top-bar-section ul li>a.button:hover,.top-bar-section ul li>a.button:focus{color:#fff}.top-bar-section ul li>a.button.secondary{background-color:#e7e7e7;border-color:#b9b9b9;color:#333}.top-bar-section ul li>a.button.secondary:hover,.top-bar-section ul li>a.button.secondary:focus{background-color:#b9b9b9}.top-bar-section ul li>a.button.secondary:hover,.top-bar-section ul li>a.button.secondary:focus{color:#333}.top-bar-section ul li>a.button.success{background-color:#43AC6A;border-color:#368a55;color:#fff}.top-bar-section ul li>a.button.success:hover,.top-bar-section ul li>a.button.success:focus{background-color:#368a55}.top-bar-section ul li>a.button.success:hover,.top-bar-section ul li>a.button.success:focus{color:#fff}.top-bar-section ul li>a.button.alert{background-color:#f04124;border-color:#cf2a0e;color:#fff}.top-bar-section ul li>a.button.alert:hover,.top-bar-section ul li>a.button.alert:focus{background-color:#cf2a0e}.top-bar-section ul li>a.button.alert:hover,.top-bar-section ul li>a.button.alert:focus{color:#fff}.top-bar-section ul li>a.button.warning{background-color:#f08a24;border-color:#cf6e0e;color:#fff}.top-bar-section ul li>a.button.warning:hover,.top-bar-section ul li>a.button.warning:focus{background-color:#cf6e0e}.top-bar-section ul li>a.button.warning:hover,.top-bar-section ul li>a.button.warning:focus{color:#fff}.top-bar-section ul li>a.button.info{background-color:#a0d3e8;border-color:#61b6d9;color:#333}.top-bar-section ul li>a.button.info:hover,.top-bar-section ul li>a.button.info:focus{background-color:#61b6d9}.top-bar-section ul li>a.button.info:hover,.top-bar-section ul li>a.button.info:focus{color:#fff}.top-bar-section ul li>button{font-size:0.8125rem;padding-left:0.9375rem;padding-right:0.9375rem;background-color:#008CBA;border-color:#007095;color:#fff}.top-bar-section ul li>button:hover,.top-bar-section ul li>button:focus{background-color:#007095}.top-bar-section ul li>button:hover,.top-bar-section ul li>button:focus{color:#fff}.top-bar-section ul li>button.secondary{background-color:#e7e7e7;border-color:#b9b9b9;color:#333}.top-bar-section ul li>button.secondary:hover,.top-bar-section ul li>button.secondary:focus{background-color:#b9b9b9}.top-bar-section ul li>button.secondary:hover,.top-bar-section ul li>button.secondary:focus{color:#333}.top-bar-section ul li>button.success{background-color:#43AC6A;border-color:#368a55;color:#fff}.top-bar-section ul li>button.success:hover,.top-bar-section ul li>button.success:focus{background-color:#368a55}.top-bar-section ul li>button.success:hover,.top-bar-section ul li>button.success:focus{color:#fff}.top-bar-section ul li>button.alert{background-color:#f04124;border-color:#cf2a0e;color:#fff}.top-bar-section ul li>button.alert:hover,.top-bar-section ul li>button.alert:focus{background-color:#cf2a0e}.top-bar-section ul li>button.alert:hover,.top-bar-section ul li>button.alert:focus{color:#fff}.top-bar-section ul li>button.warning{background-color:#f08a24;border-color:#cf6e0e;color:#fff}.top-bar-section ul li>button.warning:hover,.top-bar-section ul li>button.warning:focus{background-color:#cf6e0e}.top-bar-section ul li>button.warning:hover,.top-bar-section ul li>button.warning:focus{color:#fff}.top-bar-section ul li>button.info{background-color:#a0d3e8;border-color:#61b6d9;color:#333}.top-bar-section ul li>button.info:hover,.top-bar-section ul li>button.info:focus{background-color:#61b6d9}.top-bar-section ul li>button.info:hover,.top-bar-section ul li>button.info:focus{color:#fff}.top-bar-section ul li:hover:not(.has-form)>a{background-color:#555;color:#fff;background:#222}.top-bar-section ul li.active>a{background:#008CBA;color:#fff}.top-bar-section ul li.active>a:hover{background:#0078a0;color:#fff}.top-bar-section .has-form{padding:0.9375rem}.top-bar-section .has-dropdown{position:relative}.top-bar-section .has-dropdown>a:after{border:inset 5px;content:"";display:block;height:0;width:0;border-color:transparent transparent transparent rgba(255,255,255,0.4);border-left-style:solid;margin-right:0.9375rem;margin-top:-4.5px;position:absolute;top:50%;right:0}.top-bar-section .has-dropdown.moved{position:static}.top-bar-section .has-dropdown.moved>.dropdown{position:static !important;height:auto;width:auto;overflow:visible;clip:auto;display:block;position:absolute !important;width:100%}.top-bar-section .has-dropdown.moved>a:after{display:none}.top-bar-section .dropdown{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px;display:block;padding:0;position:absolute;top:0;z-index:99;left:100%}.top-bar-section .dropdown li{height:auto;width:100%}.top-bar-section .dropdown li a{font-weight:normal;padding:8px 0.9375rem}.top-bar-section .dropdown li a.parent-link{font-weight:normal}.top-bar-section .dropdown li.title h5,.top-bar-section .dropdown li.parent-link{margin-bottom:0;margin-top:0;font-size:1.125rem}.top-bar-section .dropdown li.title h5 a,.top-bar-section .dropdown li.parent-link a{color:#fff;display:block}.top-bar-section .dropdown li.title h5 a:hover,.top-bar-section .dropdown li.parent-link a:hover{background:none}.top-bar-section .dropdown li.has-form{padding:8px 0.9375rem}.top-bar-section .dropdown li .button,.top-bar-section .dropdown li button{top:auto}.top-bar-section .dropdown label{color:#777;font-size:0.625rem;font-weight:bold;margin-bottom:0;padding:8px 0.9375rem 2px;text-transform:uppercase}.js-generated{display:block}@media only screen and (min-width: 40.0625em){.top-bar{background:#333;overflow:visible}.top-bar:before,.top-bar:after{content:" ";display:table}.top-bar:after{clear:both}.top-bar .toggle-topbar{display:none}.top-bar .title-area{float:left}.top-bar .name h1 a,.top-bar .name h2 a,.top-bar .name h3 a,.top-bar .name h4 a,.top-bar .name h5 a,.top-bar .name h6 a{width:auto}.top-bar input,.top-bar select,.top-bar .button,.top-bar button{font-size:0.875rem;height:1.75rem;position:relative;top:0.53125rem}.top-bar.expanded{background:#333}.contain-to-grid .top-bar{margin-bottom:0;margin:0 auto;max-width:62.5rem}.top-bar-section{transition:none 0 0;left:0 !important}.top-bar-section ul{display:inline;height:auto !important;width:auto}.top-bar-section ul li{float:left}.top-bar-section ul li .js-generated{display:none}.top-bar-section li.hover>a:not(.button){background-color:#555;background:#222;color:#fff}.top-bar-section li:not(.has-form) a:not(.button){background:#333;line-height:2.8125rem;padding:0 0.9375rem}.top-bar-section li:not(.has-form) a:not(.button):hover{background-color:#555;background:#222}.top-bar-section li.active:not(.has-form) a:not(.button){background:#008CBA;color:#fff;line-height:2.8125rem;padding:0 0.9375rem}.top-bar-section li.active:not(.has-form) a:not(.button):hover{background:#0078a0;color:#fff}.top-bar-section .has-dropdown>a{padding-right:2.1875rem !important}.top-bar-section .has-dropdown>a:after{border:inset 5px;content:"";display:block;height:0;width:0;border-color:rgba(255,255,255,0.4) transparent transparent transparent;border-top-style:solid;margin-top:-2.5px;top:1.40625rem}.top-bar-section .has-dropdown.moved{position:relative}.top-bar-section .has-dropdown.moved>.dropdown{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px;display:block}.top-bar-section .has-dropdown.hover>.dropdown,.top-bar-section .has-dropdown.not-click:hover>.dropdown{position:static !important;height:auto;width:auto;overflow:visible;clip:auto;display:block;position:absolute !important}.top-bar-section .has-dropdown>a:focus+.dropdown{position:static !important;height:auto;width:auto;overflow:visible;clip:auto;display:block;position:absolute !important}.top-bar-section .has-dropdown .dropdown li.has-dropdown>a:after{border:none;content:"\00bb";top:0.1875rem;right:5px}.top-bar-section .dropdown{left:0;background:transparent;min-width:100%;top:auto}.top-bar-section .dropdown li a{background:#333;color:#fff;line-height:2.8125rem;padding:12px 0.9375rem;white-space:nowrap}.top-bar-section .dropdown li:not(.has-form):not(.active)>a:not(.button){background:#333;color:#fff}.top-bar-section .dropdown li:not(.has-form):not(.active):hover>a:not(.button){background-color:#555;color:#fff;background:#222}.top-bar-section .dropdown li label{background:#333;white-space:nowrap}.top-bar-section .dropdown li .dropdown{left:100%;top:0}.top-bar-section>ul>.divider,.top-bar-section>ul>[role="separator"]{border-right:solid 1px #4e4e4e;border-bottom:none;border-top:none;clear:none;height:2.8125rem;width:0}.top-bar-section .has-form{background:#333;height:2.8125rem;padding:0 0.9375rem}.top-bar-section .right li .dropdown{left:auto;right:0}.top-bar-section .right li .dropdown li .dropdown{right:100%}.top-bar-section .left li .dropdown{right:auto;left:0}.top-bar-section .left li .dropdown li .dropdown{left:100%}.no-js .top-bar-section ul li:hover>a{background-color:#555;background:#222;color:#fff}.no-js .top-bar-section ul li:active>a{background:#008CBA;color:#fff}.no-js .top-bar-section .has-dropdown:hover>.dropdown{position:static !important;height:auto;width:auto;overflow:visible;clip:auto;display:block;position:absolute !important}.no-js .top-bar-section .has-dropdown>a:focus+.dropdown{position:static !important;height:auto;width:auto;overflow:visible;clip:auto;display:block;position:absolute !important}}.breadcrumbs{border-style:solid;border-width:1px;display:block;list-style:none;margin-left:0;overflow:hidden;padding:0.5625rem 0.875rem 0.5625rem;background-color:#f4f4f4;border-color:#dcdcdc;border-radius:3px}.breadcrumbs>*{color:#008CBA;float:left;font-size:0.6875rem;line-height:0.6875rem;margin:0;text-transform:uppercase}.breadcrumbs>*:hover a,.breadcrumbs>*:focus a{text-decoration:underline}.breadcrumbs>* a{color:#008CBA}.breadcrumbs>*.current{color:#333;cursor:default}.breadcrumbs>*.current a{color:#333;cursor:default}.breadcrumbs>*.current:hover,.breadcrumbs>*.current:hover a,.breadcrumbs>*.current:focus,.breadcrumbs>*.current:focus a{text-decoration:none}.breadcrumbs>*.unavailable{color:#999}.breadcrumbs>*.unavailable a{color:#999}.breadcrumbs>*.unavailable:hover,.breadcrumbs>*.unavailable:hover a,.breadcrumbs>*.unavailable:focus,.breadcrumbs>*.unavailable a:focus{color:#999;cursor:not-allowed;text-decoration:none}.breadcrumbs>*:before{color:#aaa;content:"/";margin:0 0.75rem;position:relative;top:1px}.breadcrumbs>*:first-child:before{content:" ";margin:0}[aria-label="breadcrumbs"] [aria-hidden="true"]:after{content:"/"}.alert-box{border-style:solid;border-width:1px;display:block;font-size:0.8125rem;font-weight:normal;margin-bottom:1.25rem;padding:0.875rem 1.5rem 0.875rem 0.875rem;position:relative;transition:opacity 300ms ease-out;background-color:#008CBA;border-color:#0078a0;color:#fff}.alert-box .close{right:0.25rem;background:inherit;color:#333;font-size:1.375rem;line-height:.9;margin-top:-0.6875rem;opacity:0.3;padding:0 6px 4px;position:absolute;top:50%}.alert-box .close:hover,.alert-box .close:focus{opacity:0.5}.alert-box.radius{border-radius:3px}.alert-box.round{border-radius:1000px}.alert-box.success{background-color:#43AC6A;border-color:#3a945b;color:#fff}.alert-box.alert{background-color:#f04124;border-color:#de2d0f;color:#fff}.alert-box.secondary{background-color:#e7e7e7;border-color:#c7c7c7;color:#4f4f4f}.alert-box.warning{background-color:#f08a24;border-color:#de770f;color:#fff}.alert-box.info{background-color:#a0d3e8;border-color:#74bfdd;color:#4f4f4f}.alert-box.alert-close{opacity:0}.inline-list{list-style:none;margin-left:-1.375rem;margin-right:0;margin:0 auto 1.0625rem auto;overflow:hidden;padding:0}.inline-list>li{display:block;float:left;list-style:none;margin-left:1.375rem}.inline-list>li>*{display:block}.button-group{list-style:none;margin:0;left:0}.button-group:before,.button-group:after{content:" ";display:table}.button-group:after{clear:both}.button-group.even-2 li{display:inline-block;margin:0 -2px;width:50%}.button-group.even-2 li>button,.button-group.even-2 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-2 li:first-child button,.button-group.even-2 li:first-child .button{border-left:0}.button-group.even-2 li button,.button-group.even-2 li .button{width:100%}.button-group.even-3 li{display:inline-block;margin:0 -2px;width:33.33333%}.button-group.even-3 li>button,.button-group.even-3 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-3 li:first-child button,.button-group.even-3 li:first-child .button{border-left:0}.button-group.even-3 li button,.button-group.even-3 li .button{width:100%}.button-group.even-4 li{display:inline-block;margin:0 -2px;width:25%}.button-group.even-4 li>button,.button-group.even-4 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-4 li:first-child button,.button-group.even-4 li:first-child .button{border-left:0}.button-group.even-4 li button,.button-group.even-4 li .button{width:100%}.button-group.even-5 li{display:inline-block;margin:0 -2px;width:20%}.button-group.even-5 li>button,.button-group.even-5 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-5 li:first-child button,.button-group.even-5 li:first-child .button{border-left:0}.button-group.even-5 li button,.button-group.even-5 li .button{width:100%}.button-group.even-6 li{display:inline-block;margin:0 -2px;width:16.66667%}.button-group.even-6 li>button,.button-group.even-6 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-6 li:first-child button,.button-group.even-6 li:first-child .button{border-left:0}.button-group.even-6 li button,.button-group.even-6 li .button{width:100%}.button-group.even-7 li{display:inline-block;margin:0 -2px;width:14.28571%}.button-group.even-7 li>button,.button-group.even-7 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-7 li:first-child button,.button-group.even-7 li:first-child .button{border-left:0}.button-group.even-7 li button,.button-group.even-7 li .button{width:100%}.button-group.even-8 li{display:inline-block;margin:0 -2px;width:12.5%}.button-group.even-8 li>button,.button-group.even-8 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-8 li:first-child button,.button-group.even-8 li:first-child .button{border-left:0}.button-group.even-8 li button,.button-group.even-8 li .button{width:100%}.button-group>li{display:inline-block;margin:0 -2px}.button-group>li>button,.button-group>li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group>li:first-child button,.button-group>li:first-child .button{border-left:0}.button-group.stack>li{display:block;margin:0;float:none}.button-group.stack>li>button,.button-group.stack>li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.stack>li:first-child button,.button-group.stack>li:first-child .button{border-left:0}.button-group.stack>li>button,.button-group.stack>li .button{border-color:rgba(255,255,255,0.5);border-left-width:0;border-top:1px solid;display:block;margin:0}.button-group.stack>li>button{width:100%}.button-group.stack>li:first-child button,.button-group.stack>li:first-child .button{border-top:0}.button-group.stack-for-small>li{display:inline-block;margin:0 -2px}.button-group.stack-for-small>li>button,.button-group.stack-for-small>li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.stack-for-small>li:first-child button,.button-group.stack-for-small>li:first-child .button{border-left:0}@media only screen and (max-width: 40em){.button-group.stack-for-small>li{display:block;margin:0}.button-group.stack-for-small>li>button,.button-group.stack-for-small>li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.stack-for-small>li:first-child button,.button-group.stack-for-small>li:first-child .button{border-left:0}.button-group.stack-for-small>li>button,.button-group.stack-for-small>li .button{border-color:rgba(255,255,255,0.5);border-left-width:0;border-top:1px solid;display:block;margin:0}.button-group.stack-for-small>li>button{width:100%}.button-group.stack-for-small>li:first-child button,.button-group.stack-for-small>li:first-child .button{border-top:0}}.button-group.radius>*{display:inline-block;margin:0 -2px}.button-group.radius>*>button,.button-group.radius>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.radius>*:first-child button,.button-group.radius>*:first-child .button{border-left:0}.button-group.radius>*,.button-group.radius>*>a,.button-group.radius>*>button,.button-group.radius>*>.button{border-radius:0}.button-group.radius>*:first-child,.button-group.radius>*:first-child>a,.button-group.radius>*:first-child>button,.button-group.radius>*:first-child>.button{-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.button-group.radius>*:last-child,.button-group.radius>*:last-child>a,.button-group.radius>*:last-child>button,.button-group.radius>*:last-child>.button{-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.button-group.radius.stack>*{display:block;margin:0}.button-group.radius.stack>*>button,.button-group.radius.stack>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.radius.stack>*:first-child button,.button-group.radius.stack>*:first-child .button{border-left:0}.button-group.radius.stack>*>button,.button-group.radius.stack>* .button{border-color:rgba(255,255,255,0.5);border-left-width:0;border-top:1px solid;display:block;margin:0}.button-group.radius.stack>*>button{width:100%}.button-group.radius.stack>*:first-child button,.button-group.radius.stack>*:first-child .button{border-top:0}.button-group.radius.stack>*,.button-group.radius.stack>*>a,.button-group.radius.stack>*>button,.button-group.radius.stack>*>.button{border-radius:0}.button-group.radius.stack>*:first-child,.button-group.radius.stack>*:first-child>a,.button-group.radius.stack>*:first-child>button,.button-group.radius.stack>*:first-child>.button{-webkit-top-left-radius:3px;-webkit-top-right-radius:3px;border-top-left-radius:3px;border-top-right-radius:3px}.button-group.radius.stack>*:last-child,.button-group.radius.stack>*:last-child>a,.button-group.radius.stack>*:last-child>button,.button-group.radius.stack>*:last-child>.button{-webkit-bottom-left-radius:3px;-webkit-bottom-right-radius:3px;border-bottom-left-radius:3px;border-bottom-right-radius:3px}@media only screen and (min-width: 40.0625em){.button-group.radius.stack-for-small>*{display:inline-block;margin:0 -2px}.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.radius.stack-for-small>*:first-child button,.button-group.radius.stack-for-small>*:first-child .button{border-left:0}.button-group.radius.stack-for-small>*,.button-group.radius.stack-for-small>*>a,.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>*>.button{border-radius:0}.button-group.radius.stack-for-small>*:first-child,.button-group.radius.stack-for-small>*:first-child>a,.button-group.radius.stack-for-small>*:first-child>button,.button-group.radius.stack-for-small>*:first-child>.button{-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.button-group.radius.stack-for-small>*:last-child,.button-group.radius.stack-for-small>*:last-child>a,.button-group.radius.stack-for-small>*:last-child>button,.button-group.radius.stack-for-small>*:last-child>.button{-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}}@media only screen and (max-width: 40em){.button-group.radius.stack-for-small>*{display:block;margin:0}.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.radius.stack-for-small>*:first-child button,.button-group.radius.stack-for-small>*:first-child .button{border-left:0}.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>* .button{border-color:rgba(255,255,255,0.5);border-left-width:0;border-top:1px solid;display:block;margin:0}.button-group.radius.stack-for-small>*>button{width:100%}.button-group.radius.stack-for-small>*:first-child button,.button-group.radius.stack-for-small>*:first-child .button{border-top:0}.button-group.radius.stack-for-small>*,.button-group.radius.stack-for-small>*>a,.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>*>.button{border-radius:0}.button-group.radius.stack-for-small>*:first-child,.button-group.radius.stack-for-small>*:first-child>a,.button-group.radius.stack-for-small>*:first-child>button,.button-group.radius.stack-for-small>*:first-child>.button{-webkit-top-left-radius:3px;-webkit-top-right-radius:3px;border-top-left-radius:3px;border-top-right-radius:3px}.button-group.radius.stack-for-small>*:last-child,.button-group.radius.stack-for-small>*:last-child>a,.button-group.radius.stack-for-small>*:last-child>button,.button-group.radius.stack-for-small>*:last-child>.button{-webkit-bottom-left-radius:3px;-webkit-bottom-right-radius:3px;border-bottom-left-radius:3px;border-bottom-right-radius:3px}}.button-group.round>*{display:inline-block;margin:0 -2px}.button-group.round>*>button,.button-group.round>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.round>*:first-child button,.button-group.round>*:first-child .button{border-left:0}.button-group.round>*,.button-group.round>*>a,.button-group.round>*>button,.button-group.round>*>.button{border-radius:0}.button-group.round>*:first-child,.button-group.round>*:first-child>a,.button-group.round>*:first-child>button,.button-group.round>*:first-child>.button{-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}.button-group.round>*:last-child,.button-group.round>*:last-child>a,.button-group.round>*:last-child>button,.button-group.round>*:last-child>.button{-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}.button-group.round.stack>*{display:block;margin:0}.button-group.round.stack>*>button,.button-group.round.stack>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.round.stack>*:first-child button,.button-group.round.stack>*:first-child .button{border-left:0}.button-group.round.stack>*>button,.button-group.round.stack>* .button{border-color:rgba(255,255,255,0.5);border-left-width:0;border-top:1px solid;display:block;margin:0}.button-group.round.stack>*>button{width:100%}.button-group.round.stack>*:first-child button,.button-group.round.stack>*:first-child .button{border-top:0}.button-group.round.stack>*,.button-group.round.stack>*>a,.button-group.round.stack>*>button,.button-group.round.stack>*>.button{border-radius:0}.button-group.round.stack>*:first-child,.button-group.round.stack>*:first-child>a,.button-group.round.stack>*:first-child>button,.button-group.round.stack>*:first-child>.button{-webkit-top-left-radius:1rem;-webkit-top-right-radius:1rem;border-top-left-radius:1rem;border-top-right-radius:1rem}.button-group.round.stack>*:last-child,.button-group.round.stack>*:last-child>a,.button-group.round.stack>*:last-child>button,.button-group.round.stack>*:last-child>.button{-webkit-bottom-left-radius:1rem;-webkit-bottom-right-radius:1rem;border-bottom-left-radius:1rem;border-bottom-right-radius:1rem}@media only screen and (min-width: 40.0625em){.button-group.round.stack-for-small>*{display:inline-block;margin:0 -2px}.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.round.stack-for-small>*:first-child button,.button-group.round.stack-for-small>*:first-child .button{border-left:0}.button-group.round.stack-for-small>*,.button-group.round.stack-for-small>*>a,.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>*>.button{border-radius:0}.button-group.round.stack-for-small>*:first-child,.button-group.round.stack-for-small>*:first-child>a,.button-group.round.stack-for-small>*:first-child>button,.button-group.round.stack-for-small>*:first-child>.button{-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}.button-group.round.stack-for-small>*:last-child,.button-group.round.stack-for-small>*:last-child>a,.button-group.round.stack-for-small>*:last-child>button,.button-group.round.stack-for-small>*:last-child>.button{-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}}@media only screen and (max-width: 40em){.button-group.round.stack-for-small>*{display:block;margin:0}.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.round.stack-for-small>*:first-child button,.button-group.round.stack-for-small>*:first-child .button{border-left:0}.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>* .button{border-color:rgba(255,255,255,0.5);border-left-width:0;border-top:1px solid;display:block;margin:0}.button-group.round.stack-for-small>*>button{width:100%}.button-group.round.stack-for-small>*:first-child button,.button-group.round.stack-for-small>*:first-child .button{border-top:0}.button-group.round.stack-for-small>*,.button-group.round.stack-for-small>*>a,.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>*>.button{border-radius:0}.button-group.round.stack-for-small>*:first-child,.button-group.round.stack-for-small>*:first-child>a,.button-group.round.stack-for-small>*:first-child>button,.button-group.round.stack-for-small>*:first-child>.button{-webkit-top-left-radius:1rem;-webkit-top-right-radius:1rem;border-top-left-radius:1rem;border-top-right-radius:1rem}.button-group.round.stack-for-small>*:last-child,.button-group.round.stack-for-small>*:last-child>a,.button-group.round.stack-for-small>*:last-child>button,.button-group.round.stack-for-small>*:last-child>.button{-webkit-bottom-left-radius:1rem;-webkit-bottom-right-radius:1rem;border-bottom-left-radius:1rem;border-bottom-right-radius:1rem}}.button-bar:before,.button-bar:after{content:" ";display:table}.button-bar:after{clear:both}.button-bar .button-group{float:left;margin-right:0.625rem}.button-bar .button-group div{overflow:hidden}.panel{border-style:solid;border-width:1px;border-color:#d8d8d8;margin-bottom:1.25rem;padding:1.25rem;background:#f2f2f2;color:#333}.panel>:first-child{margin-top:0}.panel>:last-child{margin-bottom:0}.panel h1,.panel h2,.panel h3,.panel h4,.panel h5,.panel h6,.panel p,.panel li,.panel dl{color:#333}.panel h1,.panel h2,.panel h3,.panel h4,.panel h5,.panel h6{line-height:1;margin-bottom:0.625rem}.panel h1.subheader,.panel h2.subheader,.panel h3.subheader,.panel h4.subheader,.panel h5.subheader,.panel h6.subheader{line-height:1.4}.panel.callout{border-style:solid;border-width:1px;border-color:#d8d8d8;margin-bottom:1.25rem;padding:1.25rem;background:#ecfaff;color:#333}.panel.callout>:first-child{margin-top:0}.panel.callout>:last-child{margin-bottom:0}.panel.callout h1,.panel.callout h2,.panel.callout h3,.panel.callout h4,.panel.callout h5,.panel.callout h6,.panel.callout p,.panel.callout li,.panel.callout dl{color:#333}.panel.callout h1,.panel.callout h2,.panel.callout h3,.panel.callout h4,.panel.callout h5,.panel.callout h6{line-height:1;margin-bottom:0.625rem}.panel.callout h1.subheader,.panel.callout h2.subheader,.panel.callout h3.subheader,.panel.callout h4.subheader,.panel.callout h5.subheader,.panel.callout h6.subheader{line-height:1.4}.panel.callout a:not(.button){color:#008CBA}.panel.callout a:not(.button):hover,.panel.callout a:not(.button):focus{color:#0078a0}.panel.radius{border-radius:3px}.dropdown.button,button.dropdown{position:relative;padding-right:3.5625rem}.dropdown.button::after,button.dropdown::after{border-color:#fff transparent transparent transparent;border-style:solid;content:"";display:block;height:0;position:absolute;top:50%;width:0}.dropdown.button::after,button.dropdown::after{border-width:0.375rem;right:1.40625rem;margin-top:-0.15625rem}.dropdown.button::after,button.dropdown::after{border-color:#fff transparent transparent transparent}.dropdown.button.tiny,button.dropdown.tiny{padding-right:2.625rem}.dropdown.button.tiny:after,button.dropdown.tiny:after{border-width:0.375rem;right:1.125rem;margin-top:-0.125rem}.dropdown.button.tiny::after,button.dropdown.tiny::after{border-color:#fff transparent transparent transparent}.dropdown.button.small,button.dropdown.small{padding-right:3.0625rem}.dropdown.button.small::after,button.dropdown.small::after{border-width:0.4375rem;right:1.3125rem;margin-top:-0.15625rem}.dropdown.button.small::after,button.dropdown.small::after{border-color:#fff transparent transparent transparent}.dropdown.button.large,button.dropdown.large{padding-right:3.625rem}.dropdown.button.large::after,button.dropdown.large::after{border-width:0.3125rem;right:1.71875rem;margin-top:-0.15625rem}.dropdown.button.large::after,button.dropdown.large::after{border-color:#fff transparent transparent transparent}.dropdown.button.secondary:after,button.dropdown.secondary:after{border-color:#333 transparent transparent transparent}.th{border:solid 4px #fff;box-shadow:0 0 0 1px rgba(0,0,0,0.2);display:inline-block;line-height:0;max-width:100%;transition:all 200ms ease-out}.th:hover,.th:focus{box-shadow:0 0 6px 1px rgba(0,140,186,0.5)}.th.radius{border-radius:3px}.pricing-table{border:solid 1px #ddd;margin-left:0;margin-bottom:1.25rem}.pricing-table *{list-style:none;line-height:1}.pricing-table .title{background-color:#333;color:#eee;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:1rem;font-weight:normal;padding:0.9375rem 1.25rem;text-align:center}.pricing-table .price{background-color:#F6F6F6;color:#333;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:2rem;font-weight:normal;padding:0.9375rem 1.25rem;text-align:center}.pricing-table .description{background-color:#fff;border-bottom:dotted 1px #ddd;color:#777;font-size:0.75rem;font-weight:normal;line-height:1.4;padding:0.9375rem;text-align:center}.pricing-table .bullet-item{background-color:#fff;border-bottom:dotted 1px #ddd;color:#333;font-size:0.875rem;font-weight:normal;padding:0.9375rem;text-align:center}.pricing-table .cta-button{background-color:#fff;padding:1.25rem 1.25rem 0;text-align:center}@-webkit-keyframes rotate{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotate{from{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}}.slideshow-wrapper{position:relative}.slideshow-wrapper ul{list-style-type:none;margin:0}.slideshow-wrapper ul li,.slideshow-wrapper ul li .orbit-caption{display:none}.slideshow-wrapper ul li:first-child{display:block}.slideshow-wrapper .orbit-container{background-color:transparent}.slideshow-wrapper .orbit-container li{display:block}.slideshow-wrapper .orbit-container li .orbit-caption{display:block}.slideshow-wrapper .orbit-container .orbit-bullets li{display:inline-block}.slideshow-wrapper .preloader{border-radius:1000px;animation-duration:1.5s;animation-iteration-count:infinite;animation-name:rotate;animation-timing-function:linear;border-color:#555 #fff;border:solid 3px;display:block;height:40px;left:50%;margin-left:-20px;margin-top:-20px;position:absolute;top:50%;width:40px}.orbit-container{background:none;overflow:hidden;position:relative;width:100%}.orbit-container .orbit-slides-container{list-style:none;margin:0;padding:0;position:relative;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);-o-transform:translateZ(0);transform:translateZ(0)}.orbit-container .orbit-slides-container img{display:block;max-width:100%}.orbit-container .orbit-slides-container>*{position:absolute;top:0;width:100%;margin-left:100%}.orbit-container .orbit-slides-container>*:first-child{margin-left:0}.orbit-container .orbit-slides-container>* .orbit-caption{bottom:0;position:absolute;background-color:rgba(51,51,51,0.8);color:#fff;font-size:0.875rem;padding:0.625rem 0.875rem;width:100%}.orbit-container .orbit-slide-number{left:10px;background:transparent;color:#fff;font-size:12px;position:absolute;top:10px;z-index:10}.orbit-container .orbit-slide-number span{font-weight:700;padding:0.3125rem}.orbit-container .orbit-timer{position:absolute;top:12px;right:10px;height:6px;width:100px;z-index:10}.orbit-container .orbit-timer .orbit-progress{height:3px;background-color:rgba(255,255,255,0.3);display:block;width:0;position:relative;right:20px;top:5px}.orbit-container .orbit-timer>span{border:solid 4px #fff;border-bottom:none;border-top:none;display:none;height:14px;position:absolute;top:0;width:11px;right:0}.orbit-container .orbit-timer.paused>span{top:0;width:11px;height:14px;border:inset 8px;border-left-style:solid;border-color:transparent;border-left-color:#fff;right:-4px}.orbit-container .orbit-timer.paused>span.dark{border-left-color:#333}.orbit-container:hover .orbit-timer>span{display:block}.orbit-container .orbit-prev,.orbit-container .orbit-next{background-color:transparent;color:white;height:60px;line-height:50px;margin-top:-25px;position:absolute;text-indent:-9999px !important;top:45%;width:36px;z-index:10}.orbit-container .orbit-prev:hover,.orbit-container .orbit-next:hover{background-color:rgba(0,0,0,0.3)}.orbit-container .orbit-prev>span,.orbit-container .orbit-next>span{border:inset 10px;display:block;height:0;margin-top:-10px;position:absolute;top:50%;width:0}.orbit-container .orbit-prev{left:0}.orbit-container .orbit-prev>span{border-right-style:solid;border-color:transparent;border-right-color:#fff}.orbit-container .orbit-prev:hover>span{border-right-color:#fff}.orbit-container .orbit-next{right:0}.orbit-container .orbit-next>span{border-color:transparent;border-left-style:solid;border-left-color:#fff;left:50%;margin-left:-4px}.orbit-container .orbit-next:hover>span{border-left-color:#fff}.orbit-bullets-container{text-align:center}.orbit-bullets{display:block;float:none;margin:0 auto 30px auto;overflow:hidden;position:relative;text-align:center;top:10px}.orbit-bullets li{background:#ccc;cursor:pointer;display:inline-block;float:none;height:0.5625rem;margin-right:6px;width:0.5625rem;border-radius:1000px}.orbit-bullets li.active{background:#999}.orbit-bullets li:last-child{margin-right:0}.touch .orbit-container .orbit-prev,.touch .orbit-container .orbit-next{display:none}.touch .orbit-bullets{display:none}@media only screen and (min-width: 40.0625em){.touch .orbit-container .orbit-prev,.touch .orbit-container .orbit-next{display:inherit}.touch .orbit-bullets{display:block}}@media only screen and (max-width: 40em){.orbit-stack-on-small .orbit-slides-container{height:auto !important}.orbit-stack-on-small .orbit-slides-container>*{margin:0 !important;opacity:1 !important;position:relative}.orbit-stack-on-small .orbit-slide-number{display:none}.orbit-timer{display:none}.orbit-next,.orbit-prev{display:none}.orbit-bullets{display:none}}[data-magellan-expedition],[data-magellan-expedition-clone]{background:#fff;min-width:100%;padding:10px;z-index:50}[data-magellan-expedition] .sub-nav,[data-magellan-expedition-clone] .sub-nav{margin-bottom:0}[data-magellan-expedition] .sub-nav dd,[data-magellan-expedition-clone] .sub-nav dd{margin-bottom:0}[data-magellan-expedition] .sub-nav a,[data-magellan-expedition-clone] .sub-nav a{line-height:1.8em}.icon-bar{display:inline-block;font-size:0;width:100%;background:#333}.icon-bar>*{display:block;float:left;font-size:1rem;margin:0 auto;padding:1.25rem;text-align:center;width:25%}.icon-bar>* i,.icon-bar>* img{display:block;margin:0 auto}.icon-bar>* i+label,.icon-bar>* img+label{margin-top:.0625rem}.icon-bar>* i{font-size:1.875rem;vertical-align:middle}.icon-bar>* img{height:1.875rem;width:1.875rem}.icon-bar.label-right>* i,.icon-bar.label-right>* img{display:inline-block;margin:0 .0625rem 0 0}.icon-bar.label-right>* i+label,.icon-bar.label-right>* img+label{margin-top:0}.icon-bar.label-right>* label{display:inline-block}.icon-bar.vertical.label-right>*{text-align:left}.icon-bar.vertical,.icon-bar.small-vertical{height:100%;width:auto}.icon-bar.vertical .item,.icon-bar.small-vertical .item{float:none;margin:auto;width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.medium-vertical{height:100%;width:auto}.icon-bar.medium-vertical .item{float:none;margin:auto;width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.large-vertical{height:100%;width:auto}.icon-bar.large-vertical .item{float:none;margin:auto;width:auto}}.icon-bar>*{font-size:1rem;padding:1.25rem}.icon-bar>* i+label,.icon-bar>* img+label{margin-top:.0625rem;font-size:1rem}.icon-bar>* i{font-size:1.875rem}.icon-bar>* img{height:1.875rem;width:1.875rem}.icon-bar>* label{color:#fff}.icon-bar>* i{color:#fff}.icon-bar>a:hover{background:#008CBA}.icon-bar>a:hover label{color:#fff}.icon-bar>a:hover i{color:#fff}.icon-bar>a.active{background:#008CBA}.icon-bar>a.active label{color:#fff}.icon-bar>a.active i{color:#fff}.icon-bar .item.disabled{cursor:not-allowed;opacity:0.7;pointer-events:none}.icon-bar .item.disabled>*{opacity:0.7;cursor:not-allowed}.icon-bar.two-up .item{width:50%}.icon-bar.two-up.vertical .item,.icon-bar.two-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.two-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.two-up.large-vertical .item{width:auto}}.icon-bar.three-up .item{width:33.3333%}.icon-bar.three-up.vertical .item,.icon-bar.three-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.three-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.three-up.large-vertical .item{width:auto}}.icon-bar.four-up .item{width:25%}.icon-bar.four-up.vertical .item,.icon-bar.four-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.four-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.four-up.large-vertical .item{width:auto}}.icon-bar.five-up .item{width:20%}.icon-bar.five-up.vertical .item,.icon-bar.five-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.five-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.five-up.large-vertical .item{width:auto}}.icon-bar.six-up .item{width:16.66667%}.icon-bar.six-up.vertical .item,.icon-bar.six-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.six-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.six-up.large-vertical .item{width:auto}}.icon-bar.seven-up .item{width:14.28571%}.icon-bar.seven-up.vertical .item,.icon-bar.seven-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.seven-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.seven-up.large-vertical .item{width:auto}}.icon-bar.eight-up .item{width:12.5%}.icon-bar.eight-up.vertical .item,.icon-bar.eight-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.eight-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.eight-up.large-vertical .item{width:auto}}.icon-bar.two-up .item{width:50%}.icon-bar.two-up.vertical .item,.icon-bar.two-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.two-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.two-up.large-vertical .item{width:auto}}.icon-bar.three-up .item{width:33.3333%}.icon-bar.three-up.vertical .item,.icon-bar.three-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.three-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.three-up.large-vertical .item{width:auto}}.icon-bar.four-up .item{width:25%}.icon-bar.four-up.vertical .item,.icon-bar.four-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.four-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.four-up.large-vertical .item{width:auto}}.icon-bar.five-up .item{width:20%}.icon-bar.five-up.vertical .item,.icon-bar.five-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.five-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.five-up.large-vertical .item{width:auto}}.icon-bar.six-up .item{width:16.66667%}.icon-bar.six-up.vertical .item,.icon-bar.six-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.six-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.six-up.large-vertical .item{width:auto}}.icon-bar.seven-up .item{width:14.28571%}.icon-bar.seven-up.vertical .item,.icon-bar.seven-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.seven-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.seven-up.large-vertical .item{width:auto}}.icon-bar.eight-up .item{width:12.5%}.icon-bar.eight-up.vertical .item,.icon-bar.eight-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.0625em){.icon-bar.eight-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.0625em){.icon-bar.eight-up.large-vertical .item{width:auto}}.tabs{margin-bottom:0 !important;margin-left:0}.tabs:before,.tabs:after{content:" ";display:table}.tabs:after{clear:both}.tabs dd,.tabs .tab-title{float:left;list-style:none;margin-bottom:0 !important;position:relative}.tabs dd>a,.tabs .tab-title>a{display:block;background-color:#EFEFEF;color:#222;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:1rem;padding:1rem 2rem}.tabs dd>a:hover,.tabs .tab-title>a:hover{background-color:#e1e1e1}.tabs dd.active a,.tabs .tab-title.active a{background-color:#fff;color:#222}.tabs.radius dd:first-child a,.tabs.radius .tab:first-child a{-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.tabs.radius dd:last-child a,.tabs.radius .tab:last-child a{-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.tabs.vertical dd,.tabs.vertical .tab-title{position:inherit;float:none;display:block;top:auto}.tabs-content{margin-bottom:1.5rem;width:100%}.tabs-content:before,.tabs-content:after{content:" ";display:table}.tabs-content:after{clear:both}.tabs-content>.content{display:none;float:left;padding:0.9375rem 0;width:100%}.tabs-content>.content.active{display:block;float:none}.tabs-content>.content.contained{padding:0.9375rem}.tabs-content.vertical{display:block}.tabs-content.vertical>.content{padding:0 0.9375rem}@media only screen and (min-width: 40.0625em){.tabs.vertical{float:left;margin:0;margin-bottom:1.25rem !important;max-width:20%;width:20%}.tabs-content.vertical{float:left;margin-left:-1px;max-width:80%;padding-left:1rem;width:80%}}.no-js .tabs-content>.content{display:block;float:none}ul.pagination{display:block;margin-left:-0.3125rem;min-height:1.5rem}ul.pagination li{color:#222;font-size:0.875rem;height:1.5rem;margin-left:0.3125rem}ul.pagination li a,ul.pagination li button{border-radius:3px;transition:background-color 300ms ease-out;background:none;color:#999;display:block;font-size:1em;font-weight:normal;line-height:inherit;padding:0.0625rem 0.625rem 0.0625rem}ul.pagination li:hover a,ul.pagination li a:focus,ul.pagination li:hover button,ul.pagination li button:focus{background:#e6e6e6}ul.pagination li.unavailable a,ul.pagination li.unavailable button{cursor:default;color:#999}ul.pagination li.unavailable:hover a,ul.pagination li.unavailable a:focus,ul.pagination li.unavailable:hover button,ul.pagination li.unavailable button:focus{background:transparent}ul.pagination li.current a,ul.pagination li.current button{background:#008CBA;color:#fff;cursor:default;font-weight:bold}ul.pagination li.current a:hover,ul.pagination li.current a:focus,ul.pagination li.current button:hover,ul.pagination li.current button:focus{background:#008CBA}ul.pagination li{display:block;float:left}.pagination-centered{text-align:center}.pagination-centered ul.pagination li{display:inline-block;float:none}.side-nav{display:block;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;list-style-position:outside;list-style-type:none;margin:0;padding:0.875rem 0}.side-nav li{font-size:0.875rem;font-weight:normal;margin:0 0 0.4375rem 0}.side-nav li a:not(.button){color:#008CBA;display:block;margin:0;padding:0.4375rem 0.875rem}.side-nav li a:not(.button):hover,.side-nav li a:not(.button):focus{background:rgba(0,0,0,0.025);color:#1cc7ff}.side-nav li a:not(.button):active{color:#1cc7ff}.side-nav li.active>a:first-child:not(.button){color:#1cc7ff;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-weight:normal}.side-nav li.divider{border-top:1px solid;height:0;list-style:none;padding:0;border-top-color:#e6e6e6}.side-nav li.heading{color:#008CBA;font-size:0.875rem;font-weight:bold;text-transform:uppercase}.accordion{margin-bottom:0}.accordion:before,.accordion:after{content:" ";display:table}.accordion:after{clear:both}.accordion .accordion-navigation,.accordion dd{display:block;margin-bottom:0 !important}.accordion .accordion-navigation.active>a,.accordion dd.active>a{background:#e8e8e8}.accordion .accordion-navigation>a,.accordion dd>a{background:#EFEFEF;color:#222;display:block;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:1rem;padding:1rem}.accordion .accordion-navigation>a:hover,.accordion dd>a:hover{background:#e3e3e3}.accordion .accordion-navigation>.content,.accordion dd>.content{display:none;padding:0.9375rem}.accordion .accordion-navigation>.content.active,.accordion dd>.content.active{background:#fff;display:block}.text-left{text-align:left !important}.text-right{text-align:right !important}.text-center{text-align:center !important}.text-justify{text-align:justify !important}@media only screen and (max-width: 40em){.small-only-text-left{text-align:left !important}.small-only-text-right{text-align:right !important}.small-only-text-center{text-align:center !important}.small-only-text-justify{text-align:justify !important}}@media only screen{.small-text-left{text-align:left !important}.small-text-right{text-align:right !important}.small-text-center{text-align:center !important}.small-text-justify{text-align:justify !important}}@media only screen and (min-width: 40.0625em) and (max-width: 64em){.medium-only-text-left{text-align:left !important}.medium-only-text-right{text-align:right !important}.medium-only-text-center{text-align:center !important}.medium-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 40.0625em){.medium-text-left{text-align:left !important}.medium-text-right{text-align:right !important}.medium-text-center{text-align:center !important}.medium-text-justify{text-align:justify !important}}@media only screen and (min-width: 64.0625em) and (max-width: 90em){.large-only-text-left{text-align:left !important}.large-only-text-right{text-align:right !important}.large-only-text-center{text-align:center !important}.large-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 64.0625em){.large-text-left{text-align:left !important}.large-text-right{text-align:right !important}.large-text-center{text-align:center !important}.large-text-justify{text-align:justify !important}}@media only screen and (min-width: 90.0625em) and (max-width: 120em){.xlarge-only-text-left{text-align:left !important}.xlarge-only-text-right{text-align:right !important}.xlarge-only-text-center{text-align:center !important}.xlarge-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 90.0625em){.xlarge-text-left{text-align:left !important}.xlarge-text-right{text-align:right !important}.xlarge-text-center{text-align:center !important}.xlarge-text-justify{text-align:justify !important}}@media only screen and (min-width: 120.0625em) and (max-width: 6249999.9375em){.xxlarge-only-text-left{text-align:left !important}.xxlarge-only-text-right{text-align:right !important}.xxlarge-only-text-center{text-align:center !important}.xxlarge-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 120.0625em){.xxlarge-text-left{text-align:left !important}.xxlarge-text-right{text-align:right !important}.xxlarge-text-center{text-align:center !important}.xxlarge-text-justify{text-align:justify !important}}div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}a{color:#008CBA;line-height:inherit;text-decoration:none}a:hover,a:focus{color:#0078a0}a img{border:none}p{font-family:inherit;font-size:1rem;font-weight:normal;line-height:1.6;margin-bottom:1.25rem;text-rendering:optimizeLegibility}p.lead{font-size:1.21875rem;line-height:1.6}p aside{font-size:0.875rem;font-style:italic;line-height:1.35}h1,h2,h3,h4,h5,h6{color:#222;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-style:normal;font-weight:normal;line-height:1.4;margin-bottom:0.5rem;margin-top:0.2rem;text-rendering:optimizeLegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{color:#6f6f6f;font-size:60%;line-height:0}h1{font-size:2.125rem}h2{font-size:1.6875rem}h3{font-size:1.375rem}h4{font-size:1.125rem}h5{font-size:1.125rem}h6{font-size:1rem}.subheader{line-height:1.4;color:#6f6f6f;font-weight:normal;margin-top:0.2rem;margin-bottom:0.5rem}hr{border:solid #ddd;border-width:1px 0 0;clear:both;height:0;margin:1.25rem 0 1.1875rem}em,i{font-style:italic;line-height:inherit}strong,b{font-weight:bold;line-height:inherit}small{font-size:60%;line-height:inherit}code{background-color:#f8f8f8;border-color:#dfdfdf;border-style:solid;border-width:1px;color:#333;font-family:Consolas,"Liberation Mono",Courier,monospace;font-weight:normal;padding:0.125rem 0.3125rem 0.0625rem}ul,ol,dl{font-family:inherit;font-size:1rem;line-height:1.6;list-style-position:outside;margin-bottom:1.25rem}ul{margin-left:1.1rem}ul.no-bullet{margin-left:0}ul.no-bullet li ul,ul.no-bullet li ol{margin-left:1.25rem;margin-bottom:0;list-style:none}ul li ul,ul li ol{margin-left:1.25rem;margin-bottom:0}ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}ul.square{list-style-type:square;margin-left:1.1rem}ul.circle{list-style-type:circle;margin-left:1.1rem}ul.disc{list-style-type:disc;margin-left:1.1rem}ul.no-bullet{list-style:none}ol{margin-left:1.4rem}ol li ul,ol li ol{margin-left:1.25rem;margin-bottom:0}dl dt{margin-bottom:0.3rem;font-weight:bold}dl dd{margin-bottom:0.75rem}abbr,acronym{text-transform:uppercase;font-size:90%;color:#222;cursor:help}abbr{text-transform:none}abbr[title]{border-bottom:1px dotted #ddd}blockquote{margin:0 0 1.25rem;padding:0.5625rem 1.25rem 0 1.1875rem;border-left:1px solid #ddd}blockquote cite{display:block;font-size:0.8125rem;color:#555}blockquote cite:before{content:"\2014 \0020"}blockquote cite a,blockquote cite a:visited{color:#555}blockquote,blockquote p{line-height:1.6;color:#6f6f6f}.vcard{display:inline-block;margin:0 0 1.25rem 0;border:1px solid #ddd;padding:0.625rem 0.75rem}.vcard li{margin:0;display:block}.vcard .fn{font-weight:bold;font-size:0.9375rem}.vevent .summary{font-weight:bold}.vevent abbr{cursor:default;text-decoration:none;font-weight:bold;border:none;padding:0 0.0625rem}@media only screen and (min-width: 40.0625em){h1,h2,h3,h4,h5,h6{line-height:1.4}h1{font-size:2.75rem}h2{font-size:2.3125rem}h3{font-size:1.6875rem}h4{font-size:1.4375rem}h5{font-size:1.125rem}h6{font-size:1rem}}.split.button{position:relative;padding-right:5.0625rem}.split.button span{display:block;height:100%;position:absolute;right:0;top:0;border-left:solid 1px}.split.button span:after{position:absolute;content:"";width:0;height:0;display:block;border-style:inset;top:50%;left:50%}.split.button span:active{background-color:rgba(0,0,0,0.1)}.split.button span{border-left-color:rgba(255,255,255,0.5)}.split.button span{width:3.09375rem}.split.button span:after{border-top-style:solid;border-width:0.375rem;margin-left:-0.375rem;top:48%}.split.button span:after{border-color:#fff transparent transparent transparent}.split.button.secondary span{border-left-color:rgba(255,255,255,0.5)}.split.button.secondary span:after{border-color:#fff transparent transparent transparent}.split.button.alert span{border-left-color:rgba(255,255,255,0.5)}.split.button.success span{border-left-color:rgba(255,255,255,0.5)}.split.button.tiny{padding-right:3.75rem}.split.button.tiny span{width:2.25rem}.split.button.tiny span:after{border-top-style:solid;border-width:0.375rem;margin-left:-0.375rem;top:48%}.split.button.small{padding-right:4.375rem}.split.button.small span{width:2.625rem}.split.button.small span:after{border-top-style:solid;border-width:0.4375rem;margin-left:-0.375rem;top:48%}.split.button.large{padding-right:5.5rem}.split.button.large span{width:3.4375rem}.split.button.large span:after{border-top-style:solid;border-width:0.3125rem;margin-left:-0.375rem;top:48%}.split.button.expand{padding-left:2rem}.split.button.secondary span:after{border-color:#333 transparent transparent transparent}.split.button.radius span{-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.split.button.round span{-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}.split.button.no-pip span:before{border-style:none}.split.button.no-pip span:after{border-style:none}.split.button.no-pip span>i{display:block;left:50%;margin-left:-0.28889em;margin-top:-0.48889em;position:absolute;top:50%}.reveal-modal-bg{background:#000;background:rgba(0,0,0,0.45);bottom:0;display:none;left:0;position:fixed;right:0;top:0;z-index:1004;left:0}.reveal-modal{border-radius:3px;display:none;position:absolute;top:0;visibility:hidden;width:100%;z-index:1005;left:0;background-color:#fff;padding:1.875rem;border:solid 1px #666;box-shadow:0 0 10px rgba(0,0,0,0.4)}@media only screen and (max-width: 40em){.reveal-modal{min-height:100vh}}.reveal-modal .column,.reveal-modal .columns{min-width:0}.reveal-modal>:first-child{margin-top:0}.reveal-modal>:last-child{margin-bottom:0}@media only screen and (min-width: 40.0625em){.reveal-modal{left:0;margin:0 auto;max-width:62.5rem;right:0;width:80%}}@media only screen and (min-width: 40.0625em){.reveal-modal{top:6.25rem}}.reveal-modal.radius{border-radius:3px}.reveal-modal.round{border-radius:1000px}.reveal-modal.collapse{padding:0}@media only screen and (min-width: 40.0625em){.reveal-modal.tiny{left:0;margin:0 auto;max-width:62.5rem;right:0;width:30%}}@media only screen and (min-width: 40.0625em){.reveal-modal.small{left:0;margin:0 auto;max-width:62.5rem;right:0;width:40%}}@media only screen and (min-width: 40.0625em){.reveal-modal.medium{left:0;margin:0 auto;max-width:62.5rem;right:0;width:60%}}@media only screen and (min-width: 40.0625em){.reveal-modal.large{left:0;margin:0 auto;max-width:62.5rem;right:0;width:70%}}@media only screen and (min-width: 40.0625em){.reveal-modal.xlarge{left:0;margin:0 auto;max-width:62.5rem;right:0;width:95%}}.reveal-modal.full{height:100vh;height:100%;left:0;margin-left:0 !important;max-width:none !important;min-height:100vh;top:0}@media only screen and (min-width: 40.0625em){.reveal-modal.full{left:0;margin:0 auto;max-width:62.5rem;right:0;width:100%}}.reveal-modal.toback{z-index:1003}.reveal-modal .close-reveal-modal{color:#aaa;cursor:pointer;font-size:2.5rem;font-weight:bold;line-height:1;position:absolute;top:0.625rem;right:1.375rem}.has-tip{border-bottom:dotted 1px #ccc;color:#333;cursor:help;font-weight:bold}.has-tip:hover,.has-tip:focus{border-bottom:dotted 1px #003f54;color:#008CBA}.has-tip.tip-left,.has-tip.tip-right{float:none !important}.tooltip{background:#333;color:#fff;display:none;font-size:0.875rem;font-weight:normal;line-height:1.3;max-width:300px;padding:0.75rem;position:absolute;width:100%;z-index:1006;left:50%}.tooltip>.nub{border-color:transparent transparent #333 transparent;border:solid 5px;display:block;height:0;pointer-events:none;position:absolute;top:-10px;width:0;left:5px}.tooltip>.nub.rtl{left:auto;right:5px}.tooltip.radius{border-radius:3px}.tooltip.round{border-radius:1000px}.tooltip.round>.nub{left:2rem}.tooltip.opened{border-bottom:dotted 1px #003f54 !important;color:#008CBA !important}.tap-to-close{color:#777;display:block;font-size:0.625rem;font-weight:normal}@media only screen and (min-width: 40.0625em){.tooltip>.nub{border-color:transparent transparent #333 transparent;top:-10px}.tooltip.tip-top>.nub{border-color:#333 transparent transparent transparent;bottom:-10px;top:auto}.tooltip.tip-left,.tooltip.tip-right{float:none !important}.tooltip.tip-left>.nub{border-color:transparent transparent transparent #333;left:auto;margin-top:-5px;right:-10px;top:50%}.tooltip.tip-right>.nub{border-color:transparent #333 transparent transparent;left:-10px;margin-top:-5px;right:auto;top:50%}}.clearing-thumbs,[data-clearing]{list-style:none;margin-left:0;margin-bottom:0}.clearing-thumbs:before,.clearing-thumbs:after,[data-clearing]:before,[data-clearing]:after{content:" ";display:table}.clearing-thumbs:after,[data-clearing]:after{clear:both}.clearing-thumbs li,[data-clearing] li{float:left;margin-right:10px}.clearing-thumbs[class*="block-grid-"] li,[data-clearing][class*="block-grid-"] li{margin-right:0}.clearing-blackout{background:#333;height:100%;position:fixed;top:0;width:100%;z-index:998;left:0}.clearing-blackout .clearing-close{display:block}.clearing-container{height:100%;margin:0;overflow:hidden;position:relative;z-index:998}.clearing-touch-label{color:#aaa;font-size:.6em;left:50%;position:absolute;top:50%}.visible-img{height:95%;position:relative}.visible-img img{position:absolute;left:50%;top:50%;-webkit-transform:translateY(-50%) translateX(-50%);-moz-transform:translateY(-50%) translateX(-50%);-ms-transform:translateY(-50%) translateX(-50%);-o-transform:translateY(-50%) translateX(-50%);transform:translateY(-50%) translateX(-50%);max-height:100%;max-width:100%}.clearing-caption{background:#333;bottom:0;color:#ccc;font-size:0.875em;line-height:1.3;margin-bottom:0;padding:10px 30px 20px;position:absolute;text-align:center;width:100%;left:0}.clearing-close{color:#ccc;display:none;font-size:30px;line-height:1;padding-left:20px;padding-top:10px;z-index:999}.clearing-close:hover,.clearing-close:focus{color:#ccc}.clearing-assembled .clearing-container{height:100%}.clearing-assembled .clearing-container .carousel>ul{display:none}.clearing-feature li{display:none}.clearing-feature li.clearing-featured-img{display:block}@media only screen and (min-width: 40.0625em){.clearing-main-prev,.clearing-main-next{height:100%;position:absolute;top:0;width:40px}.clearing-main-prev>span,.clearing-main-next>span{border:solid 12px;display:block;height:0;position:absolute;top:50%;width:0}.clearing-main-prev>span:hover,.clearing-main-next>span:hover{opacity:.8}.clearing-main-prev{left:0}.clearing-main-prev>span{left:5px;border-color:transparent;border-right-color:#ccc}.clearing-main-next{right:0}.clearing-main-next>span{border-color:transparent;border-left-color:#ccc}.clearing-main-prev.disabled,.clearing-main-next.disabled{opacity:.3}.clearing-assembled .clearing-container .carousel{background:rgba(51,51,51,0.8);height:120px;margin-top:10px;text-align:center}.clearing-assembled .clearing-container .carousel>ul{display:inline-block;z-index:999;height:100%;position:relative;float:none}.clearing-assembled .clearing-container .carousel>ul li{clear:none;cursor:pointer;display:block;float:left;margin-right:0;min-height:inherit;opacity:.4;overflow:hidden;padding:0;position:relative;width:120px}.clearing-assembled .clearing-container .carousel>ul li.fix-height img{height:100%;max-width:none}.clearing-assembled .clearing-container .carousel>ul li a.th{border:none;box-shadow:none;display:block}.clearing-assembled .clearing-container .carousel>ul li img{cursor:pointer !important;width:100% !important}.clearing-assembled .clearing-container .carousel>ul li.visible{opacity:1}.clearing-assembled .clearing-container .carousel>ul li:hover{opacity:.8}.clearing-assembled .clearing-container .visible-img{background:#333;height:85%;overflow:hidden}.clearing-close{padding-left:0;padding-top:0;position:absolute;top:10px;right:20px}}.progress{background-color:#F6F6F6;border:1px solid #fff;height:1.5625rem;margin-bottom:0.625rem;padding:0.125rem}.progress .meter{background:#008CBA;display:block;height:100%}.progress.secondary .meter{background:#e7e7e7;display:block;height:100%}.progress.success .meter{background:#43AC6A;display:block;height:100%}.progress.alert .meter{background:#f04124;display:block;height:100%}.progress.radius{border-radius:3px}.progress.radius .meter{border-radius:2px}.progress.round{border-radius:1000px}.progress.round .meter{border-radius:999px}.sub-nav{display:block;margin:-0.25rem 0 1.125rem;overflow:hidden;padding-top:0.25rem;width:auto}.sub-nav dt{text-transform:uppercase}.sub-nav dt,.sub-nav dd,.sub-nav li{color:#999;float:left;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:0.875rem;font-weight:normal;margin-left:1rem;margin-bottom:0}.sub-nav dt a,.sub-nav dd a,.sub-nav li a{color:#999;padding:0.1875rem 1rem;text-decoration:none}.sub-nav dt a:hover,.sub-nav dd a:hover,.sub-nav li a:hover{color:#737373}.sub-nav dt.active a,.sub-nav dd.active a,.sub-nav li.active a{border-radius:3px;background:#008CBA;color:#fff;cursor:default;font-weight:normal;padding:0.1875rem 1rem}.sub-nav dt.active a:hover,.sub-nav dd.active a:hover,.sub-nav li.active a:hover{background:#0078a0}.joyride-list{display:none}.joyride-tip-guide{background:#333;color:#fff;display:none;font-family:inherit;font-weight:normal;position:absolute;top:0;width:95%;z-index:101;left:2.5%}.lt-ie9 .joyride-tip-guide{margin-left:-400px;max-width:800px;left:50%}.joyride-content-wrapper{padding:1.125rem 1.25rem 1.5rem;width:100%}.joyride-content-wrapper .button{margin-bottom:0 !important}.joyride-content-wrapper .joyride-prev-tip{margin-right:10px}.joyride-tip-guide .joyride-nub{border:10px solid #333;display:block;height:0;position:absolute;width:0;left:22px}.joyride-tip-guide .joyride-nub.top{border-color:#333;border-top-color:transparent !important;border-top-style:solid;border-left-color:transparent !important;border-right-color:transparent !important;top:-20px}.joyride-tip-guide .joyride-nub.bottom{border-color:#333 !important;border-bottom-color:transparent !important;border-bottom-style:solid;border-left-color:transparent !important;border-right-color:transparent !important;bottom:-20px}.joyride-tip-guide .joyride-nub.right{right:-20px}.joyride-tip-guide .joyride-nub.left{left:-20px}.joyride-tip-guide h1,.joyride-tip-guide h2,.joyride-tip-guide h3,.joyride-tip-guide h4,.joyride-tip-guide h5,.joyride-tip-guide h6{color:#fff;font-weight:bold;line-height:1.25;margin:0}.joyride-tip-guide p{font-size:0.875rem;line-height:1.3;margin:0 0 1.125rem 0}.joyride-timer-indicator-wrap{border:solid 1px #555;bottom:1rem;height:3px;position:absolute;width:50px;right:1.0625rem}.joyride-timer-indicator{background:#666;display:block;height:inherit;width:0}.joyride-close-tip{color:#777 !important;font-size:24px;font-weight:normal;line-height:.5 !important;position:absolute;text-decoration:none;top:10px;right:12px}.joyride-close-tip:hover,.joyride-close-tip:focus{color:#eee !important}.joyride-modal-bg{background:rgba(0,0,0,0.5);cursor:pointer;display:none;height:100%;position:fixed;top:0;width:100%;z-index:100;left:0}.joyride-expose-wrapper{background-color:#fff;border-radius:3px;box-shadow:0 0 15px #fff;position:absolute;z-index:102}.joyride-expose-cover{background:transparent;border-radius:3px;left:0;position:absolute;top:0;z-index:9999}@media only screen and (min-width: 40.0625em){.joyride-tip-guide{width:300px;left:inherit}.joyride-tip-guide .joyride-nub.bottom{border-color:#333 !important;border-bottom-color:transparent !important;border-left-color:transparent !important;border-right-color:transparent !important;bottom:-20px}.joyride-tip-guide .joyride-nub.right{border-color:#333 !important;border-right-color:transparent !important;border-bottom-color:transparent !important;border-top-color:transparent !important;left:auto;right:-20px;top:22px}.joyride-tip-guide .joyride-nub.left{border-color:#333 !important;border-bottom-color:transparent !important;border-left-color:transparent !important;border-top-color:transparent !important;left:-20px;right:auto;top:22px}}.label{display:inline-block;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-weight:normal;line-height:1;margin-bottom:auto;position:relative;text-align:center;text-decoration:none;white-space:nowrap;padding:0.25rem 0.5rem 0.25rem;font-size:0.6875rem;background-color:#008CBA;color:#fff}.label.radius{border-radius:3px}.label.round{border-radius:1000px}.label.alert{background-color:#f04124;color:#fff}.label.warning{background-color:#f08a24;color:#fff}.label.success{background-color:#43AC6A;color:#fff}.label.secondary{background-color:#e7e7e7;color:#333}.label.info{background-color:#a0d3e8;color:#333}.off-canvas-wrap{-webkit-backface-visibility:hidden;position:relative;width:100%;overflow:hidden}.off-canvas-wrap.move-right,.off-canvas-wrap.move-left{min-height:100%;-webkit-overflow-scrolling:touch}.inner-wrap{position:relative;width:100%;-webkit-transition:-webkit-transform 500ms ease;-moz-transition:-moz-transform 500ms ease;-ms-transition:-ms-transform 500ms ease;-o-transition:-o-transform 500ms ease;transition:transform 500ms ease}.inner-wrap:before,.inner-wrap:after{content:" ";display:table}.inner-wrap:after{clear:both}.tab-bar{-webkit-backface-visibility:hidden;background:#333;color:#fff;height:2.8125rem;line-height:2.8125rem;position:relative}.tab-bar h1,.tab-bar h2,.tab-bar h3,.tab-bar h4,.tab-bar h5,.tab-bar h6{color:#fff;font-weight:bold;line-height:2.8125rem;margin:0}.tab-bar h1,.tab-bar h2,.tab-bar h3,.tab-bar h4{font-size:1.125rem}.left-small{height:2.8125rem;position:absolute;top:0;width:2.8125rem;border-right:solid 1px #1a1a1a;left:0}.right-small{height:2.8125rem;position:absolute;top:0;width:2.8125rem;border-left:solid 1px #1a1a1a;right:0}.tab-bar-section{height:2.8125rem;padding:0 0.625rem;position:absolute;text-align:center;top:0}.tab-bar-section.left{text-align:left}.tab-bar-section.right{text-align:right}.tab-bar-section.left{left:0;right:2.8125rem}.tab-bar-section.right{left:2.8125rem;right:0}.tab-bar-section.middle{left:2.8125rem;right:2.8125rem}.tab-bar .menu-icon{color:#fff;display:block;height:2.8125rem;padding:0;position:relative;text-indent:2.1875rem;transform:translate3d(0, 0, 0);width:2.8125rem}.tab-bar .menu-icon span::after{content:"";display:block;height:0;position:absolute;top:50%;margin-top:-0.5rem;left:0.90625rem;box-shadow:0 0 0 1px #fff,0 7px 0 1px #fff,0 14px 0 1px #fff;width:1rem}.tab-bar .menu-icon span:hover:after{box-shadow:0 0 0 1px #b3b3b3,0 7px 0 1px #b3b3b3,0 14px 0 1px #b3b3b3}.left-off-canvas-menu{-webkit-backface-visibility:hidden;background:#333;bottom:0;box-sizing:content-box;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;overflow-x:hidden;overflow-y:auto;position:absolute;top:0;transition:transform 500ms ease 0s;width:15.625rem;z-index:1001;-webkit-transform:translate3d(-100%, 0, 0);-moz-transform:translate3d(-100%, 0, 0);-ms-transform:translate(-100%, 0);-ms-transform:translate3d(-100%, 0, 0);-o-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.left-off-canvas-menu *{-webkit-backface-visibility:hidden}.right-off-canvas-menu{-webkit-backface-visibility:hidden;background:#333;bottom:0;box-sizing:content-box;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;overflow-x:hidden;overflow-y:auto;position:absolute;top:0;transition:transform 500ms ease 0s;width:15.625rem;z-index:1001;-webkit-transform:translate3d(100%, 0, 0);-moz-transform:translate3d(100%, 0, 0);-ms-transform:translate(100%, 0);-ms-transform:translate3d(100%, 0, 0);-o-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);right:0}.right-off-canvas-menu *{-webkit-backface-visibility:hidden}ul.off-canvas-list{list-style-type:none;margin:0;padding:0}ul.off-canvas-list li label{background:#444;border-bottom:none;border-top:1px solid #5e5e5e;color:#999;display:block;font-size:0.75rem;font-weight:bold;margin:0;padding:0.3rem 0.9375rem;text-transform:uppercase}ul.off-canvas-list li a{border-bottom:1px solid #262626;color:rgba(255,255,255,0.7);display:block;padding:0.66667rem;transition:background 300ms ease}ul.off-canvas-list li a:hover{background:#242424}ul.off-canvas-list li a:active{background:#242424}.move-right>.inner-wrap{-webkit-transform:translate3d(15.625rem, 0, 0);-moz-transform:translate3d(15.625rem, 0, 0);-ms-transform:translate(15.625rem, 0);-ms-transform:translate3d(15.625rem, 0, 0);-o-transform:translate3d(15.625rem, 0, 0);transform:translate3d(15.625rem, 0, 0)}.move-right .exit-off-canvas{-webkit-backface-visibility:hidden;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);cursor:pointer;transition:background 300ms ease;-webkit-tap-highlight-color:transparent;background:rgba(255,255,255,0.2);bottom:0;display:block;left:0;position:absolute;right:0;top:0;z-index:1002}@media only screen and (min-width: 40.0625em){.move-right .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.move-left>.inner-wrap{-webkit-transform:translate3d(-15.625rem, 0, 0);-moz-transform:translate3d(-15.625rem, 0, 0);-ms-transform:translate(-15.625rem, 0);-ms-transform:translate3d(-15.625rem, 0, 0);-o-transform:translate3d(-15.625rem, 0, 0);transform:translate3d(-15.625rem, 0, 0)}.move-left .exit-off-canvas{-webkit-backface-visibility:hidden;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);cursor:pointer;transition:background 300ms ease;-webkit-tap-highlight-color:transparent;background:rgba(255,255,255,0.2);bottom:0;display:block;left:0;position:absolute;right:0;top:0;z-index:1002}@media only screen and (min-width: 40.0625em){.move-left .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.offcanvas-overlap .left-off-canvas-menu,.offcanvas-overlap .right-off-canvas-menu{-ms-transform:none;-webkit-transform:none;-moz-transform:none;-o-transform:none;transform:none;z-index:1003}.offcanvas-overlap .exit-off-canvas{-webkit-backface-visibility:hidden;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);cursor:pointer;transition:background 300ms ease;-webkit-tap-highlight-color:transparent;background:rgba(255,255,255,0.2);bottom:0;display:block;left:0;position:absolute;right:0;top:0;z-index:1002}@media only screen and (min-width: 40.0625em){.offcanvas-overlap .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.offcanvas-overlap-left .right-off-canvas-menu{-ms-transform:none;-webkit-transform:none;-moz-transform:none;-o-transform:none;transform:none;z-index:1003}.offcanvas-overlap-left .exit-off-canvas{-webkit-backface-visibility:hidden;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);cursor:pointer;transition:background 300ms ease;-webkit-tap-highlight-color:transparent;background:rgba(255,255,255,0.2);bottom:0;display:block;left:0;position:absolute;right:0;top:0;z-index:1002}@media only screen and (min-width: 40.0625em){.offcanvas-overlap-left .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.offcanvas-overlap-right .left-off-canvas-menu{-ms-transform:none;-webkit-transform:none;-moz-transform:none;-o-transform:none;transform:none;z-index:1003}.offcanvas-overlap-right .exit-off-canvas{-webkit-backface-visibility:hidden;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);cursor:pointer;transition:background 300ms ease;-webkit-tap-highlight-color:transparent;background:rgba(255,255,255,0.2);bottom:0;display:block;left:0;position:absolute;right:0;top:0;z-index:1002}@media only screen and (min-width: 40.0625em){.offcanvas-overlap-right .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.no-csstransforms .left-off-canvas-menu{left:-15.625rem}.no-csstransforms .right-off-canvas-menu{right:-15.625rem}.no-csstransforms .move-left>.inner-wrap{right:15.625rem}.no-csstransforms .move-right>.inner-wrap{left:15.625rem}.left-submenu{-webkit-backface-visibility:hidden;-webkit-overflow-scrolling:touch;background:#333;bottom:0;box-sizing:content-box;margin:0;overflow-x:hidden;overflow-y:auto;position:absolute;top:0;width:15.625rem;z-index:1002;-webkit-transform:translate3d(-100%, 0, 0);-moz-transform:translate3d(-100%, 0, 0);-ms-transform:translate(-100%, 0);-ms-transform:translate3d(-100%, 0, 0);-o-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0;-webkit-transition:-webkit-transform 500ms ease;-moz-transition:-moz-transform 500ms ease;-ms-transition:-ms-transform 500ms ease;-o-transition:-o-transform 500ms ease;transition:transform 500ms ease}.left-submenu *{-webkit-backface-visibility:hidden}.left-submenu .back>a{background:#444;border-bottom:none;border-top:1px solid #5e5e5e;color:#999;font-weight:bold;padding:0.3rem 0.9375rem;text-transform:uppercase;margin:0}.left-submenu .back>a:hover{background:#303030;border-bottom:none;border-top:1px solid #5e5e5e}.left-submenu .back>a:before{content:"\AB";margin-right:.5rem;display:inline}.left-submenu.move-right,.left-submenu.offcanvas-overlap-right,.left-submenu.offcanvas-overlap{-webkit-transform:translate3d(0%, 0, 0);-moz-transform:translate3d(0%, 0, 0);-ms-transform:translate(0%, 0);-ms-transform:translate3d(0%, 0, 0);-o-transform:translate3d(0%, 0, 0);transform:translate3d(0%, 0, 0)}.right-submenu{-webkit-backface-visibility:hidden;-webkit-overflow-scrolling:touch;background:#333;bottom:0;box-sizing:content-box;margin:0;overflow-x:hidden;overflow-y:auto;position:absolute;top:0;width:15.625rem;z-index:1002;-webkit-transform:translate3d(100%, 0, 0);-moz-transform:translate3d(100%, 0, 0);-ms-transform:translate(100%, 0);-ms-transform:translate3d(100%, 0, 0);-o-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);right:0;-webkit-transition:-webkit-transform 500ms ease;-moz-transition:-moz-transform 500ms ease;-ms-transition:-ms-transform 500ms ease;-o-transition:-o-transform 500ms ease;transition:transform 500ms ease}.right-submenu *{-webkit-backface-visibility:hidden}.right-submenu .back>a{background:#444;border-bottom:none;border-top:1px solid #5e5e5e;color:#999;font-weight:bold;padding:0.3rem 0.9375rem;text-transform:uppercase;margin:0}.right-submenu .back>a:hover{background:#303030;border-bottom:none;border-top:1px solid #5e5e5e}.right-submenu .back>a:after{content:"\BB";margin-left:.5rem;display:inline}.right-submenu.move-left,.right-submenu.offcanvas-overlap-left,.right-submenu.offcanvas-overlap{-webkit-transform:translate3d(0%, 0, 0);-moz-transform:translate3d(0%, 0, 0);-ms-transform:translate(0%, 0);-ms-transform:translate3d(0%, 0, 0);-o-transform:translate3d(0%, 0, 0);transform:translate3d(0%, 0, 0)}.left-off-canvas-menu ul.off-canvas-list li.has-submenu>a:after{content:"\BB";margin-left:.5rem;display:inline}.right-off-canvas-menu ul.off-canvas-list li.has-submenu>a:before{content:"\AB";margin-right:.5rem;display:inline}.f-dropdown{display:none;left:-9999px;list-style:none;margin-left:0;position:absolute;background:#fff;border:solid 1px #ccc;font-size:0.875rem;height:auto;max-height:none;width:100%;z-index:89;margin-top:2px;max-width:200px}.f-dropdown.open{display:block}.f-dropdown>*:first-child{margin-top:0}.f-dropdown>*:last-child{margin-bottom:0}.f-dropdown:before{border:inset 6px;content:"";display:block;height:0;width:0;border-color:transparent transparent #fff transparent;border-bottom-style:solid;position:absolute;top:-12px;left:10px;z-index:89}.f-dropdown:after{border:inset 7px;content:"";display:block;height:0;width:0;border-color:transparent transparent #ccc transparent;border-bottom-style:solid;position:absolute;top:-14px;left:9px;z-index:88}.f-dropdown.right:before{left:auto;right:10px}.f-dropdown.right:after{left:auto;right:9px}.f-dropdown.drop-right{display:none;left:-9999px;list-style:none;margin-left:0;position:absolute;background:#fff;border:solid 1px #ccc;font-size:0.875rem;height:auto;max-height:none;width:100%;z-index:89;margin-top:0;margin-left:2px;max-width:200px}.f-dropdown.drop-right.open{display:block}.f-dropdown.drop-right>*:first-child{margin-top:0}.f-dropdown.drop-right>*:last-child{margin-bottom:0}.f-dropdown.drop-right:before{border:inset 6px;content:"";display:block;height:0;width:0;border-color:transparent #fff transparent transparent;border-right-style:solid;position:absolute;top:10px;left:-12px;z-index:89}.f-dropdown.drop-right:after{border:inset 7px;content:"";display:block;height:0;width:0;border-color:transparent #ccc transparent transparent;border-right-style:solid;position:absolute;top:9px;left:-14px;z-index:88}.f-dropdown.drop-left{display:none;left:-9999px;list-style:none;margin-left:0;position:absolute;background:#fff;border:solid 1px #ccc;font-size:0.875rem;height:auto;max-height:none;width:100%;z-index:89;margin-top:0;margin-left:-2px;max-width:200px}.f-dropdown.drop-left.open{display:block}.f-dropdown.drop-left>*:first-child{margin-top:0}.f-dropdown.drop-left>*:last-child{margin-bottom:0}.f-dropdown.drop-left:before{border:inset 6px;content:"";display:block;height:0;width:0;border-color:transparent transparent transparent #fff;border-left-style:solid;position:absolute;top:10px;right:-12px;left:auto;z-index:89}.f-dropdown.drop-left:after{border:inset 7px;content:"";display:block;height:0;width:0;border-color:transparent transparent transparent #ccc;border-left-style:solid;position:absolute;top:9px;right:-14px;left:auto;z-index:88}.f-dropdown.drop-top{display:none;left:-9999px;list-style:none;margin-left:0;position:absolute;background:#fff;border:solid 1px #ccc;font-size:0.875rem;height:auto;max-height:none;width:100%;z-index:89;margin-left:0;margin-top:-2px;max-width:200px}.f-dropdown.drop-top.open{display:block}.f-dropdown.drop-top>*:first-child{margin-top:0}.f-dropdown.drop-top>*:last-child{margin-bottom:0}.f-dropdown.drop-top:before{border:inset 6px;content:"";display:block;height:0;width:0;border-color:#fff transparent transparent transparent;border-top-style:solid;bottom:-12px;position:absolute;top:auto;left:10px;right:auto;z-index:89}.f-dropdown.drop-top:after{border:inset 7px;content:"";display:block;height:0;width:0;border-color:#ccc transparent transparent transparent;border-top-style:solid;bottom:-14px;position:absolute;top:auto;left:9px;right:auto;z-index:88}.f-dropdown li{cursor:pointer;font-size:0.875rem;line-height:1.125rem;margin:0}.f-dropdown li:hover,.f-dropdown li:focus{background:#eee}.f-dropdown li.radius{border-radius:3px}.f-dropdown li a{display:block;padding:0.5rem;color:#555}.f-dropdown.content{display:none;left:-9999px;list-style:none;margin-left:0;position:absolute;background:#fff;border:solid 1px #ccc;font-size:0.875rem;height:auto;max-height:none;padding:1.25rem;width:100%;z-index:89;max-width:200px}.f-dropdown.content.open{display:block}.f-dropdown.content>*:first-child{margin-top:0}.f-dropdown.content>*:last-child{margin-bottom:0}.f-dropdown.tiny{max-width:200px}.f-dropdown.small{max-width:300px}.f-dropdown.medium{max-width:500px}.f-dropdown.large{max-width:800px}.f-dropdown.mega{width:100% !important;max-width:100% !important}.f-dropdown.mega.open{left:0 !important}table{background:#fff;border:solid 1px #ddd;margin-bottom:1.25rem;table-layout:auto}table caption{background:transparent;color:#222;font-size:1rem;font-weight:bold}table thead{background:#F5F5F5}table thead tr th,table thead tr td{color:#222;font-size:0.875rem;font-weight:bold;padding:0.5rem 0.625rem 0.625rem}table tfoot{background:#F5F5F5}table tfoot tr th,table tfoot tr td{color:#222;font-size:0.875rem;font-weight:bold;padding:0.5rem 0.625rem 0.625rem}table tr th,table tr td{color:#222;font-size:0.875rem;padding:0.5625rem 0.625rem;text-align:left}table tr.even,table tr.alt,table tr:nth-of-type(even){background:#F9F9F9}table thead tr th,table tfoot tr th,table tfoot tr td,table tbody tr th,table tbody tr td,table tr td{display:table-cell;line-height:1.125rem}.range-slider{border:1px solid #ddd;margin:1.25rem 0;position:relative;-ms-touch-action:none;touch-action:none;display:block;height:1rem;width:100%;background:#FAFAFA}.range-slider.vertical-range{border:1px solid #ddd;margin:1.25rem 0;position:relative;-ms-touch-action:none;touch-action:none;display:inline-block;height:12.5rem;width:1rem}.range-slider.vertical-range .range-slider-handle{bottom:-10.5rem;margin-left:-0.5rem;margin-top:0;position:absolute}.range-slider.vertical-range .range-slider-active-segment{border-bottom-left-radius:inherit;border-bottom-right-radius:inherit;border-top-left-radius:initial;bottom:0;height:auto;width:0.875rem}.range-slider.radius{background:#FAFAFA;border-radius:3px}.range-slider.radius .range-slider-handle{background:#008CBA;border-radius:3px}.range-slider.radius .range-slider-handle:hover{background:#007ba4}.range-slider.round{background:#FAFAFA;border-radius:1000px}.range-slider.round .range-slider-handle{background:#008CBA;border-radius:1000px}.range-slider.round .range-slider-handle:hover{background:#007ba4}.range-slider.disabled,.range-slider[disabled]{background:#FAFAFA;cursor:not-allowed;opacity:0.7}.range-slider.disabled .range-slider-handle,.range-slider[disabled] .range-slider-handle{background:#008CBA;cursor:default;opacity:0.7}.range-slider.disabled .range-slider-handle:hover,.range-slider[disabled] .range-slider-handle:hover{background:#007ba4}.range-slider-active-segment{background:#e5e5e5;border-bottom-left-radius:inherit;border-top-left-radius:inherit;display:inline-block;height:0.875rem;position:absolute}.range-slider-handle{border:1px solid none;cursor:pointer;display:inline-block;height:1.375rem;position:absolute;top:-0.3125rem;width:2rem;z-index:1;-ms-touch-action:manipulation;touch-action:manipulation;background:#008CBA}.range-slider-handle:hover{background:#007ba4}[class*="block-grid-"]{display:block;padding:0;margin:0 -0.625rem}[class*="block-grid-"]:before,[class*="block-grid-"]:after{content:" ";display:table}[class*="block-grid-"]:after{clear:both}[class*="block-grid-"]>li{display:block;float:left;height:auto;padding:0 0.625rem 1.25rem}@media only screen{.small-block-grid-1>li{list-style:none;width:100%}.small-block-grid-1>li:nth-of-type(1n){clear:none}.small-block-grid-1>li:nth-of-type(1n+1){clear:both}.small-block-grid-2>li{list-style:none;width:50%}.small-block-grid-2>li:nth-of-type(1n){clear:none}.small-block-grid-2>li:nth-of-type(2n+1){clear:both}.small-block-grid-3>li{list-style:none;width:33.33333%}.small-block-grid-3>li:nth-of-type(1n){clear:none}.small-block-grid-3>li:nth-of-type(3n+1){clear:both}.small-block-grid-4>li{list-style:none;width:25%}.small-block-grid-4>li:nth-of-type(1n){clear:none}.small-block-grid-4>li:nth-of-type(4n+1){clear:both}.small-block-grid-5>li{list-style:none;width:20%}.small-block-grid-5>li:nth-of-type(1n){clear:none}.small-block-grid-5>li:nth-of-type(5n+1){clear:both}.small-block-grid-6>li{list-style:none;width:16.66667%}.small-block-grid-6>li:nth-of-type(1n){clear:none}.small-block-grid-6>li:nth-of-type(6n+1){clear:both}.small-block-grid-7>li{list-style:none;width:14.28571%}.small-block-grid-7>li:nth-of-type(1n){clear:none}.small-block-grid-7>li:nth-of-type(7n+1){clear:both}.small-block-grid-8>li{list-style:none;width:12.5%}.small-block-grid-8>li:nth-of-type(1n){clear:none}.small-block-grid-8>li:nth-of-type(8n+1){clear:both}.small-block-grid-9>li{list-style:none;width:11.11111%}.small-block-grid-9>li:nth-of-type(1n){clear:none}.small-block-grid-9>li:nth-of-type(9n+1){clear:both}.small-block-grid-10>li{list-style:none;width:10%}.small-block-grid-10>li:nth-of-type(1n){clear:none}.small-block-grid-10>li:nth-of-type(10n+1){clear:both}.small-block-grid-11>li{list-style:none;width:9.09091%}.small-block-grid-11>li:nth-of-type(1n){clear:none}.small-block-grid-11>li:nth-of-type(11n+1){clear:both}.small-block-grid-12>li{list-style:none;width:8.33333%}.small-block-grid-12>li:nth-of-type(1n){clear:none}.small-block-grid-12>li:nth-of-type(12n+1){clear:both}}@media only screen and (min-width: 40.0625em){.medium-block-grid-1>li{list-style:none;width:100%}.medium-block-grid-1>li:nth-of-type(1n){clear:none}.medium-block-grid-1>li:nth-of-type(1n+1){clear:both}.medium-block-grid-2>li{list-style:none;width:50%}.medium-block-grid-2>li:nth-of-type(1n){clear:none}.medium-block-grid-2>li:nth-of-type(2n+1){clear:both}.medium-block-grid-3>li{list-style:none;width:33.33333%}.medium-block-grid-3>li:nth-of-type(1n){clear:none}.medium-block-grid-3>li:nth-of-type(3n+1){clear:both}.medium-block-grid-4>li{list-style:none;width:25%}.medium-block-grid-4>li:nth-of-type(1n){clear:none}.medium-block-grid-4>li:nth-of-type(4n+1){clear:both}.medium-block-grid-5>li{list-style:none;width:20%}.medium-block-grid-5>li:nth-of-type(1n){clear:none}.medium-block-grid-5>li:nth-of-type(5n+1){clear:both}.medium-block-grid-6>li{list-style:none;width:16.66667%}.medium-block-grid-6>li:nth-of-type(1n){clear:none}.medium-block-grid-6>li:nth-of-type(6n+1){clear:both}.medium-block-grid-7>li{list-style:none;width:14.28571%}.medium-block-grid-7>li:nth-of-type(1n){clear:none}.medium-block-grid-7>li:nth-of-type(7n+1){clear:both}.medium-block-grid-8>li{list-style:none;width:12.5%}.medium-block-grid-8>li:nth-of-type(1n){clear:none}.medium-block-grid-8>li:nth-of-type(8n+1){clear:both}.medium-block-grid-9>li{list-style:none;width:11.11111%}.medium-block-grid-9>li:nth-of-type(1n){clear:none}.medium-block-grid-9>li:nth-of-type(9n+1){clear:both}.medium-block-grid-10>li{list-style:none;width:10%}.medium-block-grid-10>li:nth-of-type(1n){clear:none}.medium-block-grid-10>li:nth-of-type(10n+1){clear:both}.medium-block-grid-11>li{list-style:none;width:9.09091%}.medium-block-grid-11>li:nth-of-type(1n){clear:none}.medium-block-grid-11>li:nth-of-type(11n+1){clear:both}.medium-block-grid-12>li{list-style:none;width:8.33333%}.medium-block-grid-12>li:nth-of-type(1n){clear:none}.medium-block-grid-12>li:nth-of-type(12n+1){clear:both}}@media only screen and (min-width: 64.0625em){.large-block-grid-1>li{list-style:none;width:100%}.large-block-grid-1>li:nth-of-type(1n){clear:none}.large-block-grid-1>li:nth-of-type(1n+1){clear:both}.large-block-grid-2>li{list-style:none;width:50%}.large-block-grid-2>li:nth-of-type(1n){clear:none}.large-block-grid-2>li:nth-of-type(2n+1){clear:both}.large-block-grid-3>li{list-style:none;width:33.33333%}.large-block-grid-3>li:nth-of-type(1n){clear:none}.large-block-grid-3>li:nth-of-type(3n+1){clear:both}.large-block-grid-4>li{list-style:none;width:25%}.large-block-grid-4>li:nth-of-type(1n){clear:none}.large-block-grid-4>li:nth-of-type(4n+1){clear:both}.large-block-grid-5>li{list-style:none;width:20%}.large-block-grid-5>li:nth-of-type(1n){clear:none}.large-block-grid-5>li:nth-of-type(5n+1){clear:both}.large-block-grid-6>li{list-style:none;width:16.66667%}.large-block-grid-6>li:nth-of-type(1n){clear:none}.large-block-grid-6>li:nth-of-type(6n+1){clear:both}.large-block-grid-7>li{list-style:none;width:14.28571%}.large-block-grid-7>li:nth-of-type(1n){clear:none}.large-block-grid-7>li:nth-of-type(7n+1){clear:both}.large-block-grid-8>li{list-style:none;width:12.5%}.large-block-grid-8>li:nth-of-type(1n){clear:none}.large-block-grid-8>li:nth-of-type(8n+1){clear:both}.large-block-grid-9>li{list-style:none;width:11.11111%}.large-block-grid-9>li:nth-of-type(1n){clear:none}.large-block-grid-9>li:nth-of-type(9n+1){clear:both}.large-block-grid-10>li{list-style:none;width:10%}.large-block-grid-10>li:nth-of-type(1n){clear:none}.large-block-grid-10>li:nth-of-type(10n+1){clear:both}.large-block-grid-11>li{list-style:none;width:9.09091%}.large-block-grid-11>li:nth-of-type(1n){clear:none}.large-block-grid-11>li:nth-of-type(11n+1){clear:both}.large-block-grid-12>li{list-style:none;width:8.33333%}.large-block-grid-12>li:nth-of-type(1n){clear:none}.large-block-grid-12>li:nth-of-type(12n+1){clear:both}}.flex-video{height:0;margin-bottom:1rem;overflow:hidden;padding-bottom:67.5%;padding-top:1.5625rem;position:relative}.flex-video.widescreen{padding-bottom:56.34%}.flex-video.vimeo{padding-top:0}.flex-video iframe,.flex-video object,.flex-video embed,.flex-video video{height:100%;position:absolute;top:0;width:100%;left:0}.keystroke,kbd{background-color:#ededed;border-color:#ddd;color:#222;border-style:solid;border-width:1px;font-family:"Consolas","Menlo","Courier",monospace;font-size:inherit;margin:0;padding:0.125rem 0.25rem 0;border-radius:3px}.switch{border:none;margin-bottom:1.5rem;outline:0;padding:0;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.switch label{background:#ddd;color:transparent;cursor:pointer;display:block;margin-bottom:1rem;position:relative;text-indent:100%;width:4rem;height:2rem;transition:left 0.15s ease-out}.switch input{left:10px;opacity:0;padding:0;position:absolute;top:9px}.switch input+label{margin-left:0;margin-right:0}.switch label:after{background:#fff;content:"";display:block;height:1.5rem;left:.25rem;position:absolute;top:.25rem;width:1.5rem;-webkit-transition:left 0.15s ease-out;-moz-transition:left 0.15s ease-out;-o-transition:translate3d(0, 0, 0);transition:left 0.15s ease-out;-webkit-transform:translate3d(0, 0, 0);-moz-transform:translate3d(0, 0, 0);-ms-transform:translate3d(0, 0, 0);-o-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}.switch input:checked+label{background:#008CBA}.switch input:checked+label:after{left:2.25rem}.switch label{height:2rem;width:4rem}.switch label:after{height:1.5rem;width:1.5rem}.switch input:checked+label:after{left:2.25rem}.switch label{color:transparent;background:#ddd}.switch label:after{background:#fff}.switch input:checked+label{background:#008CBA}.switch.large label{height:2.5rem;width:5rem}.switch.large label:after{height:2rem;width:2rem}.switch.large input:checked+label:after{left:2.75rem}.switch.small label{height:1.75rem;width:3.5rem}.switch.small label:after{height:1.25rem;width:1.25rem}.switch.small input:checked+label:after{left:2rem}.switch.tiny label{height:1.5rem;width:3rem}.switch.tiny label:after{height:1rem;width:1rem}.switch.tiny input:checked+label:after{left:1.75rem}.switch.radius label{border-radius:4px}.switch.radius label:after{border-radius:3px}.switch.round{border-radius:1000px}.switch.round label{border-radius:2rem}.switch.round label:after{border-radius:2rem}@media only screen{.show-for-small-only,.show-for-small-up,.show-for-small,.show-for-small-down,.hide-for-medium-only,.hide-for-medium-up,.hide-for-medium,.show-for-medium-down,.hide-for-large-only,.hide-for-large-up,.hide-for-large,.show-for-large-down,.hide-for-xlarge-only,.hide-for-xlarge-up,.hide-for-xlarge,.show-for-xlarge-down,.hide-for-xxlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge,.show-for-xxlarge-down{display:inherit !important}.hide-for-small-only,.hide-for-small-up,.hide-for-small,.hide-for-small-down,.show-for-medium-only,.show-for-medium-up,.show-for-medium,.hide-for-medium-down,.show-for-large-only,.show-for-large-up,.show-for-large,.hide-for-large-down,.show-for-xlarge-only,.show-for-xlarge-up,.show-for-xlarge,.hide-for-xlarge-down,.show-for-xxlarge-only,.show-for-xxlarge-up,.show-for-xxlarge,.hide-for-xxlarge-down{display:none !important}.visible-for-small-only,.visible-for-small-up,.visible-for-small,.visible-for-small-down,.hidden-for-medium-only,.hidden-for-medium-up,.hidden-for-medium,.visible-for-medium-down,.hidden-for-large-only,.hidden-for-large-up,.hidden-for-large,.visible-for-large-down,.hidden-for-xlarge-only,.hidden-for-xlarge-up,.hidden-for-xlarge,.visible-for-xlarge-down,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up,.hidden-for-xxlarge,.visible-for-xxlarge-down{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.hidden-for-small-only,.hidden-for-small-up,.hidden-for-small,.hidden-for-small-down,.visible-for-medium-only,.visible-for-medium-up,.visible-for-medium,.hidden-for-medium-down,.visible-for-large-only,.visible-for-large-up,.visible-for-large,.hidden-for-large-down,.visible-for-xlarge-only,.visible-for-xlarge-up,.visible-for-xlarge,.hidden-for-xlarge-down,.visible-for-xxlarge-only,.visible-for-xxlarge-up,.visible-for-xxlarge,.hidden-for-xxlarge-down{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px}table.show-for-small-only,table.show-for-small-up,table.show-for-small,table.show-for-small-down,table.hide-for-medium-only,table.hide-for-medium-up,table.hide-for-medium,table.show-for-medium-down,table.hide-for-large-only,table.hide-for-large-up,table.hide-for-large,table.show-for-large-down,table.hide-for-xlarge-only,table.hide-for-xlarge-up,table.hide-for-xlarge,table.show-for-xlarge-down,table.hide-for-xxlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge,table.show-for-xxlarge-down{display:table !important}thead.show-for-small-only,thead.show-for-small-up,thead.show-for-small,thead.show-for-small-down,thead.hide-for-medium-only,thead.hide-for-medium-up,thead.hide-for-medium,thead.show-for-medium-down,thead.hide-for-large-only,thead.hide-for-large-up,thead.hide-for-large,thead.show-for-large-down,thead.hide-for-xlarge-only,thead.hide-for-xlarge-up,thead.hide-for-xlarge,thead.show-for-xlarge-down,thead.hide-for-xxlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge,thead.show-for-xxlarge-down{display:table-header-group !important}tbody.show-for-small-only,tbody.show-for-small-up,tbody.show-for-small,tbody.show-for-small-down,tbody.hide-for-medium-only,tbody.hide-for-medium-up,tbody.hide-for-medium,tbody.show-for-medium-down,tbody.hide-for-large-only,tbody.hide-for-large-up,tbody.hide-for-large,tbody.show-for-large-down,tbody.hide-for-xlarge-only,tbody.hide-for-xlarge-up,tbody.hide-for-xlarge,tbody.show-for-xlarge-down,tbody.hide-for-xxlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge,tbody.show-for-xxlarge-down{display:table-row-group !important}tr.show-for-small-only,tr.show-for-small-up,tr.show-for-small,tr.show-for-small-down,tr.hide-for-medium-only,tr.hide-for-medium-up,tr.hide-for-medium,tr.show-for-medium-down,tr.hide-for-large-only,tr.hide-for-large-up,tr.hide-for-large,tr.show-for-large-down,tr.hide-for-xlarge-only,tr.hide-for-xlarge-up,tr.hide-for-xlarge,tr.show-for-xlarge-down,tr.hide-for-xxlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge,tr.show-for-xxlarge-down{display:table-row}th.show-for-small-only,td.show-for-small-only,th.show-for-small-up,td.show-for-small-up,th.show-for-small,td.show-for-small,th.show-for-small-down,td.show-for-small-down,th.hide-for-medium-only,td.hide-for-medium-only,th.hide-for-medium-up,td.hide-for-medium-up,th.hide-for-medium,td.hide-for-medium,th.show-for-medium-down,td.show-for-medium-down,th.hide-for-large-only,td.hide-for-large-only,th.hide-for-large-up,td.hide-for-large-up,th.hide-for-large,td.hide-for-large,th.show-for-large-down,td.show-for-large-down,th.hide-for-xlarge-only,td.hide-for-xlarge-only,th.hide-for-xlarge-up,td.hide-for-xlarge-up,th.hide-for-xlarge,td.hide-for-xlarge,th.show-for-xlarge-down,td.show-for-xlarge-down,th.hide-for-xxlarge-only,td.hide-for-xxlarge-only,th.hide-for-xxlarge-up,td.hide-for-xxlarge-up,th.hide-for-xxlarge,td.hide-for-xxlarge,th.show-for-xxlarge-down,td.show-for-xxlarge-down{display:table-cell !important}}@media only screen and (min-width: 40.0625em){.hide-for-small-only,.show-for-small-up,.hide-for-small,.hide-for-small-down,.show-for-medium-only,.show-for-medium-up,.show-for-medium,.show-for-medium-down,.hide-for-large-only,.hide-for-large-up,.hide-for-large,.show-for-large-down,.hide-for-xlarge-only,.hide-for-xlarge-up,.hide-for-xlarge,.show-for-xlarge-down,.hide-for-xxlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge,.show-for-xxlarge-down{display:inherit !important}.show-for-small-only,.hide-for-small-up,.show-for-small,.show-for-small-down,.hide-for-medium-only,.hide-for-medium-up,.hide-for-medium,.hide-for-medium-down,.show-for-large-only,.show-for-large-up,.show-for-large,.hide-for-large-down,.show-for-xlarge-only,.show-for-xlarge-up,.show-for-xlarge,.hide-for-xlarge-down,.show-for-xxlarge-only,.show-for-xxlarge-up,.show-for-xxlarge,.hide-for-xxlarge-down{display:none !important}.hidden-for-small-only,.visible-for-small-up,.hidden-for-small,.hidden-for-small-down,.visible-for-medium-only,.visible-for-medium-up,.visible-for-medium,.visible-for-medium-down,.hidden-for-large-only,.hidden-for-large-up,.hidden-for-large,.visible-for-large-down,.hidden-for-xlarge-only,.hidden-for-xlarge-up,.hidden-for-xlarge,.visible-for-xlarge-down,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up,.hidden-for-xxlarge,.visible-for-xxlarge-down{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.visible-for-small-only,.hidden-for-small-up,.visible-for-small,.visible-for-small-down,.hidden-for-medium-only,.hidden-for-medium-up,.hidden-for-medium,.hidden-for-medium-down,.visible-for-large-only,.visible-for-large-up,.visible-for-large,.hidden-for-large-down,.visible-for-xlarge-only,.visible-for-xlarge-up,.visible-for-xlarge,.hidden-for-xlarge-down,.visible-for-xxlarge-only,.visible-for-xxlarge-up,.visible-for-xxlarge,.hidden-for-xxlarge-down{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px}table.hide-for-small-only,table.show-for-small-up,table.hide-for-small,table.hide-for-small-down,table.show-for-medium-only,table.show-for-medium-up,table.show-for-medium,table.show-for-medium-down,table.hide-for-large-only,table.hide-for-large-up,table.hide-for-large,table.show-for-large-down,table.hide-for-xlarge-only,table.hide-for-xlarge-up,table.hide-for-xlarge,table.show-for-xlarge-down,table.hide-for-xxlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge,table.show-for-xxlarge-down{display:table !important}thead.hide-for-small-only,thead.show-for-small-up,thead.hide-for-small,thead.hide-for-small-down,thead.show-for-medium-only,thead.show-for-medium-up,thead.show-for-medium,thead.show-for-medium-down,thead.hide-for-large-only,thead.hide-for-large-up,thead.hide-for-large,thead.show-for-large-down,thead.hide-for-xlarge-only,thead.hide-for-xlarge-up,thead.hide-for-xlarge,thead.show-for-xlarge-down,thead.hide-for-xxlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge,thead.show-for-xxlarge-down{display:table-header-group !important}tbody.hide-for-small-only,tbody.show-for-small-up,tbody.hide-for-small,tbody.hide-for-small-down,tbody.show-for-medium-only,tbody.show-for-medium-up,tbody.show-for-medium,tbody.show-for-medium-down,tbody.hide-for-large-only,tbody.hide-for-large-up,tbody.hide-for-large,tbody.show-for-large-down,tbody.hide-for-xlarge-only,tbody.hide-for-xlarge-up,tbody.hide-for-xlarge,tbody.show-for-xlarge-down,tbody.hide-for-xxlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge,tbody.show-for-xxlarge-down{display:table-row-group !important}tr.hide-for-small-only,tr.show-for-small-up,tr.hide-for-small,tr.hide-for-small-down,tr.show-for-medium-only,tr.show-for-medium-up,tr.show-for-medium,tr.show-for-medium-down,tr.hide-for-large-only,tr.hide-for-large-up,tr.hide-for-large,tr.show-for-large-down,tr.hide-for-xlarge-only,tr.hide-for-xlarge-up,tr.hide-for-xlarge,tr.show-for-xlarge-down,tr.hide-for-xxlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge,tr.show-for-xxlarge-down{display:table-row}th.hide-for-small-only,td.hide-for-small-only,th.show-for-small-up,td.show-for-small-up,th.hide-for-small,td.hide-for-small,th.hide-for-small-down,td.hide-for-small-down,th.show-for-medium-only,td.show-for-medium-only,th.show-for-medium-up,td.show-for-medium-up,th.show-for-medium,td.show-for-medium,th.show-for-medium-down,td.show-for-medium-down,th.hide-for-large-only,td.hide-for-large-only,th.hide-for-large-up,td.hide-for-large-up,th.hide-for-large,td.hide-for-large,th.show-for-large-down,td.show-for-large-down,th.hide-for-xlarge-only,td.hide-for-xlarge-only,th.hide-for-xlarge-up,td.hide-for-xlarge-up,th.hide-for-xlarge,td.hide-for-xlarge,th.show-for-xlarge-down,td.show-for-xlarge-down,th.hide-for-xxlarge-only,td.hide-for-xxlarge-only,th.hide-for-xxlarge-up,td.hide-for-xxlarge-up,th.hide-for-xxlarge,td.hide-for-xxlarge,th.show-for-xxlarge-down,td.show-for-xxlarge-down{display:table-cell !important}}@media only screen and (min-width: 64.0625em){.hide-for-small-only,.show-for-small-up,.hide-for-small,.hide-for-small-down,.hide-for-medium-only,.show-for-medium-up,.hide-for-medium,.hide-for-medium-down,.show-for-large-only,.show-for-large-up,.show-for-large,.show-for-large-down,.hide-for-xlarge-only,.hide-for-xlarge-up,.hide-for-xlarge,.show-for-xlarge-down,.hide-for-xxlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge,.show-for-xxlarge-down{display:inherit !important}.show-for-small-only,.hide-for-small-up,.show-for-small,.show-for-small-down,.show-for-medium-only,.hide-for-medium-up,.show-for-medium,.show-for-medium-down,.hide-for-large-only,.hide-for-large-up,.hide-for-large,.hide-for-large-down,.show-for-xlarge-only,.show-for-xlarge-up,.show-for-xlarge,.hide-for-xlarge-down,.show-for-xxlarge-only,.show-for-xxlarge-up,.show-for-xxlarge,.hide-for-xxlarge-down{display:none !important}.hidden-for-small-only,.visible-for-small-up,.hidden-for-small,.hidden-for-small-down,.hidden-for-medium-only,.visible-for-medium-up,.hidden-for-medium,.hidden-for-medium-down,.visible-for-large-only,.visible-for-large-up,.visible-for-large,.visible-for-large-down,.hidden-for-xlarge-only,.hidden-for-xlarge-up,.hidden-for-xlarge,.visible-for-xlarge-down,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up,.hidden-for-xxlarge,.visible-for-xxlarge-down{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.visible-for-small-only,.hidden-for-small-up,.visible-for-small,.visible-for-small-down,.visible-for-medium-only,.hidden-for-medium-up,.visible-for-medium,.visible-for-medium-down,.hidden-for-large-only,.hidden-for-large-up,.hidden-for-large,.hidden-for-large-down,.visible-for-xlarge-only,.visible-for-xlarge-up,.visible-for-xlarge,.hidden-for-xlarge-down,.visible-for-xxlarge-only,.visible-for-xxlarge-up,.visible-for-xxlarge,.hidden-for-xxlarge-down{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px}table.hide-for-small-only,table.show-for-small-up,table.hide-for-small,table.hide-for-small-down,table.hide-for-medium-only,table.show-for-medium-up,table.hide-for-medium,table.hide-for-medium-down,table.show-for-large-only,table.show-for-large-up,table.show-for-large,table.show-for-large-down,table.hide-for-xlarge-only,table.hide-for-xlarge-up,table.hide-for-xlarge,table.show-for-xlarge-down,table.hide-for-xxlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge,table.show-for-xxlarge-down{display:table !important}thead.hide-for-small-only,thead.show-for-small-up,thead.hide-for-small,thead.hide-for-small-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.hide-for-medium,thead.hide-for-medium-down,thead.show-for-large-only,thead.show-for-large-up,thead.show-for-large,thead.show-for-large-down,thead.hide-for-xlarge-only,thead.hide-for-xlarge-up,thead.hide-for-xlarge,thead.show-for-xlarge-down,thead.hide-for-xxlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge,thead.show-for-xxlarge-down{display:table-header-group !important}tbody.hide-for-small-only,tbody.show-for-small-up,tbody.hide-for-small,tbody.hide-for-small-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.show-for-large-only,tbody.show-for-large-up,tbody.show-for-large,tbody.show-for-large-down,tbody.hide-for-xlarge-only,tbody.hide-for-xlarge-up,tbody.hide-for-xlarge,tbody.show-for-xlarge-down,tbody.hide-for-xxlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge,tbody.show-for-xxlarge-down{display:table-row-group !important}tr.hide-for-small-only,tr.show-for-small-up,tr.hide-for-small,tr.hide-for-small-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.hide-for-medium,tr.hide-for-medium-down,tr.show-for-large-only,tr.show-for-large-up,tr.show-for-large,tr.show-for-large-down,tr.hide-for-xlarge-only,tr.hide-for-xlarge-up,tr.hide-for-xlarge,tr.show-for-xlarge-down,tr.hide-for-xxlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge,tr.show-for-xxlarge-down{display:table-row}th.hide-for-small-only,td.hide-for-small-only,th.show-for-small-up,td.show-for-small-up,th.hide-for-small,td.hide-for-small,th.hide-for-small-down,td.hide-for-small-down,th.hide-for-medium-only,td.hide-for-medium-only,th.show-for-medium-up,td.show-for-medium-up,th.hide-for-medium,td.hide-for-medium,th.hide-for-medium-down,td.hide-for-medium-down,th.show-for-large-only,td.show-for-large-only,th.show-for-large-up,td.show-for-large-up,th.show-for-large,td.show-for-large,th.show-for-large-down,td.show-for-large-down,th.hide-for-xlarge-only,td.hide-for-xlarge-only,th.hide-for-xlarge-up,td.hide-for-xlarge-up,th.hide-for-xlarge,td.hide-for-xlarge,th.show-for-xlarge-down,td.show-for-xlarge-down,th.hide-for-xxlarge-only,td.hide-for-xxlarge-only,th.hide-for-xxlarge-up,td.hide-for-xxlarge-up,th.hide-for-xxlarge,td.hide-for-xxlarge,th.show-for-xxlarge-down,td.show-for-xxlarge-down{display:table-cell !important}}@media only screen and (min-width: 90.0625em){.hide-for-small-only,.show-for-small-up,.hide-for-small,.hide-for-small-down,.hide-for-medium-only,.show-for-medium-up,.hide-for-medium,.hide-for-medium-down,.hide-for-large-only,.show-for-large-up,.hide-for-large,.hide-for-large-down,.show-for-xlarge-only,.show-for-xlarge-up,.show-for-xlarge,.show-for-xlarge-down,.hide-for-xxlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge,.show-for-xxlarge-down{display:inherit !important}.show-for-small-only,.hide-for-small-up,.show-for-small,.show-for-small-down,.show-for-medium-only,.hide-for-medium-up,.show-for-medium,.show-for-medium-down,.show-for-large-only,.hide-for-large-up,.show-for-large,.show-for-large-down,.hide-for-xlarge-only,.hide-for-xlarge-up,.hide-for-xlarge,.hide-for-xlarge-down,.show-for-xxlarge-only,.show-for-xxlarge-up,.show-for-xxlarge,.hide-for-xxlarge-down{display:none !important}.hidden-for-small-only,.visible-for-small-up,.hidden-for-small,.hidden-for-small-down,.hidden-for-medium-only,.visible-for-medium-up,.hidden-for-medium,.hidden-for-medium-down,.hidden-for-large-only,.visible-for-large-up,.hidden-for-large,.hidden-for-large-down,.visible-for-xlarge-only,.visible-for-xlarge-up,.visible-for-xlarge,.visible-for-xlarge-down,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up,.hidden-for-xxlarge,.visible-for-xxlarge-down{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.visible-for-small-only,.hidden-for-small-up,.visible-for-small,.visible-for-small-down,.visible-for-medium-only,.hidden-for-medium-up,.visible-for-medium,.visible-for-medium-down,.visible-for-large-only,.hidden-for-large-up,.visible-for-large,.visible-for-large-down,.hidden-for-xlarge-only,.hidden-for-xlarge-up,.hidden-for-xlarge,.hidden-for-xlarge-down,.visible-for-xxlarge-only,.visible-for-xxlarge-up,.visible-for-xxlarge,.hidden-for-xxlarge-down{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px}table.hide-for-small-only,table.show-for-small-up,table.hide-for-small,table.hide-for-small-down,table.hide-for-medium-only,table.show-for-medium-up,table.hide-for-medium,table.hide-for-medium-down,table.hide-for-large-only,table.show-for-large-up,table.hide-for-large,table.hide-for-large-down,table.show-for-xlarge-only,table.show-for-xlarge-up,table.show-for-xlarge,table.show-for-xlarge-down,table.hide-for-xxlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge,table.show-for-xxlarge-down{display:table !important}thead.hide-for-small-only,thead.show-for-small-up,thead.hide-for-small,thead.hide-for-small-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.hide-for-medium,thead.hide-for-medium-down,thead.hide-for-large-only,thead.show-for-large-up,thead.hide-for-large,thead.hide-for-large-down,thead.show-for-xlarge-only,thead.show-for-xlarge-up,thead.show-for-xlarge,thead.show-for-xlarge-down,thead.hide-for-xxlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge,thead.show-for-xxlarge-down{display:table-header-group !important}tbody.hide-for-small-only,tbody.show-for-small-up,tbody.hide-for-small,tbody.hide-for-small-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.hide-for-large-only,tbody.show-for-large-up,tbody.hide-for-large,tbody.hide-for-large-down,tbody.show-for-xlarge-only,tbody.show-for-xlarge-up,tbody.show-for-xlarge,tbody.show-for-xlarge-down,tbody.hide-for-xxlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge,tbody.show-for-xxlarge-down{display:table-row-group !important}tr.hide-for-small-only,tr.show-for-small-up,tr.hide-for-small,tr.hide-for-small-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.hide-for-medium,tr.hide-for-medium-down,tr.hide-for-large-only,tr.show-for-large-up,tr.hide-for-large,tr.hide-for-large-down,tr.show-for-xlarge-only,tr.show-for-xlarge-up,tr.show-for-xlarge,tr.show-for-xlarge-down,tr.hide-for-xxlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge,tr.show-for-xxlarge-down{display:table-row}th.hide-for-small-only,td.hide-for-small-only,th.show-for-small-up,td.show-for-small-up,th.hide-for-small,td.hide-for-small,th.hide-for-small-down,td.hide-for-small-down,th.hide-for-medium-only,td.hide-for-medium-only,th.show-for-medium-up,td.show-for-medium-up,th.hide-for-medium,td.hide-for-medium,th.hide-for-medium-down,td.hide-for-medium-down,th.hide-for-large-only,td.hide-for-large-only,th.show-for-large-up,td.show-for-large-up,th.hide-for-large,td.hide-for-large,th.hide-for-large-down,td.hide-for-large-down,th.show-for-xlarge-only,td.show-for-xlarge-only,th.show-for-xlarge-up,td.show-for-xlarge-up,th.show-for-xlarge,td.show-for-xlarge,th.show-for-xlarge-down,td.show-for-xlarge-down,th.hide-for-xxlarge-only,td.hide-for-xxlarge-only,th.hide-for-xxlarge-up,td.hide-for-xxlarge-up,th.hide-for-xxlarge,td.hide-for-xxlarge,th.show-for-xxlarge-down,td.show-for-xxlarge-down{display:table-cell !important}}@media only screen and (min-width: 120.0625em){.hide-for-small-only,.show-for-small-up,.hide-for-small,.hide-for-small-down,.hide-for-medium-only,.show-for-medium-up,.hide-for-medium,.hide-for-medium-down,.hide-for-large-only,.show-for-large-up,.hide-for-large,.hide-for-large-down,.hide-for-xlarge-only,.show-for-xlarge-up,.hide-for-xlarge,.hide-for-xlarge-down,.show-for-xxlarge-only,.show-for-xxlarge-up,.show-for-xxlarge,.show-for-xxlarge-down{display:inherit !important}.show-for-small-only,.hide-for-small-up,.show-for-small,.show-for-small-down,.show-for-medium-only,.hide-for-medium-up,.show-for-medium,.show-for-medium-down,.show-for-large-only,.hide-for-large-up,.show-for-large,.show-for-large-down,.show-for-xlarge-only,.hide-for-xlarge-up,.show-for-xlarge,.show-for-xlarge-down,.hide-for-xxlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge,.hide-for-xxlarge-down{display:none !important}.hidden-for-small-only,.visible-for-small-up,.hidden-for-small,.hidden-for-small-down,.hidden-for-medium-only,.visible-for-medium-up,.hidden-for-medium,.hidden-for-medium-down,.hidden-for-large-only,.visible-for-large-up,.hidden-for-large,.hidden-for-large-down,.hidden-for-xlarge-only,.visible-for-xlarge-up,.hidden-for-xlarge,.hidden-for-xlarge-down,.visible-for-xxlarge-only,.visible-for-xxlarge-up,.visible-for-xxlarge,.visible-for-xxlarge-down{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.visible-for-small-only,.hidden-for-small-up,.visible-for-small,.visible-for-small-down,.visible-for-medium-only,.hidden-for-medium-up,.visible-for-medium,.visible-for-medium-down,.visible-for-large-only,.hidden-for-large-up,.visible-for-large,.visible-for-large-down,.visible-for-xlarge-only,.hidden-for-xlarge-up,.visible-for-xlarge,.visible-for-xlarge-down,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up,.hidden-for-xxlarge,.hidden-for-xxlarge-down{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px}table.hide-for-small-only,table.show-for-small-up,table.hide-for-small,table.hide-for-small-down,table.hide-for-medium-only,table.show-for-medium-up,table.hide-for-medium,table.hide-for-medium-down,table.hide-for-large-only,table.show-for-large-up,table.hide-for-large,table.hide-for-large-down,table.hide-for-xlarge-only,table.show-for-xlarge-up,table.hide-for-xlarge,table.hide-for-xlarge-down,table.show-for-xxlarge-only,table.show-for-xxlarge-up,table.show-for-xxlarge,table.show-for-xxlarge-down{display:table !important}thead.hide-for-small-only,thead.show-for-small-up,thead.hide-for-small,thead.hide-for-small-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.hide-for-medium,thead.hide-for-medium-down,thead.hide-for-large-only,thead.show-for-large-up,thead.hide-for-large,thead.hide-for-large-down,thead.hide-for-xlarge-only,thead.show-for-xlarge-up,thead.hide-for-xlarge,thead.hide-for-xlarge-down,thead.show-for-xxlarge-only,thead.show-for-xxlarge-up,thead.show-for-xxlarge,thead.show-for-xxlarge-down{display:table-header-group !important}tbody.hide-for-small-only,tbody.show-for-small-up,tbody.hide-for-small,tbody.hide-for-small-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.hide-for-large-only,tbody.show-for-large-up,tbody.hide-for-large,tbody.hide-for-large-down,tbody.hide-for-xlarge-only,tbody.show-for-xlarge-up,tbody.hide-for-xlarge,tbody.hide-for-xlarge-down,tbody.show-for-xxlarge-only,tbody.show-for-xxlarge-up,tbody.show-for-xxlarge,tbody.show-for-xxlarge-down{display:table-row-group !important}tr.hide-for-small-only,tr.show-for-small-up,tr.hide-for-small,tr.hide-for-small-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.hide-for-medium,tr.hide-for-medium-down,tr.hide-for-large-only,tr.show-for-large-up,tr.hide-for-large,tr.hide-for-large-down,tr.hide-for-xlarge-only,tr.show-for-xlarge-up,tr.hide-for-xlarge,tr.hide-for-xlarge-down,tr.show-for-xxlarge-only,tr.show-for-xxlarge-up,tr.show-for-xxlarge,tr.show-for-xxlarge-down{display:table-row}th.hide-for-small-only,td.hide-for-small-only,th.show-for-small-up,td.show-for-small-up,th.hide-for-small,td.hide-for-small,th.hide-for-small-down,td.hide-for-small-down,th.hide-for-medium-only,td.hide-for-medium-only,th.show-for-medium-up,td.show-for-medium-up,th.hide-for-medium,td.hide-for-medium,th.hide-for-medium-down,td.hide-for-medium-down,th.hide-for-large-only,td.hide-for-large-only,th.show-for-large-up,td.show-for-large-up,th.hide-for-large,td.hide-for-large,th.hide-for-large-down,td.hide-for-large-down,th.hide-for-xlarge-only,td.hide-for-xlarge-only,th.show-for-xlarge-up,td.show-for-xlarge-up,th.hide-for-xlarge,td.hide-for-xlarge,th.hide-for-xlarge-down,td.hide-for-xlarge-down,th.show-for-xxlarge-only,td.show-for-xxlarge-only,th.show-for-xxlarge-up,td.show-for-xxlarge-up,th.show-for-xxlarge,td.show-for-xxlarge,th.show-for-xxlarge-down,td.show-for-xxlarge-down{display:table-cell !important}}.show-for-landscape,.hide-for-portrait{display:inherit !important}.hide-for-landscape,.show-for-portrait{display:none !important}table.hide-for-landscape,table.show-for-portrait{display:table !important}thead.hide-for-landscape,thead.show-for-portrait{display:table-header-group !important}tbody.hide-for-landscape,tbody.show-for-portrait{display:table-row-group !important}tr.hide-for-landscape,tr.show-for-portrait{display:table-row !important}td.hide-for-landscape,td.show-for-portrait,th.hide-for-landscape,th.show-for-portrait{display:table-cell !important}@media only screen and (orientation: landscape){.show-for-landscape,.hide-for-portrait{display:inherit !important}.hide-for-landscape,.show-for-portrait{display:none !important}table.show-for-landscape,table.hide-for-portrait{display:table !important}thead.show-for-landscape,thead.hide-for-portrait{display:table-header-group !important}tbody.show-for-landscape,tbody.hide-for-portrait{display:table-row-group !important}tr.show-for-landscape,tr.hide-for-portrait{display:table-row !important}td.show-for-landscape,td.hide-for-portrait,th.show-for-landscape,th.hide-for-portrait{display:table-cell !important}}@media only screen and (orientation: portrait){.show-for-portrait,.hide-for-landscape{display:inherit !important}.hide-for-portrait,.show-for-landscape{display:none !important}table.show-for-portrait,table.hide-for-landscape{display:table !important}thead.show-for-portrait,thead.hide-for-landscape{display:table-header-group !important}tbody.show-for-portrait,tbody.hide-for-landscape{display:table-row-group !important}tr.show-for-portrait,tr.hide-for-landscape{display:table-row !important}td.show-for-portrait,td.hide-for-landscape,th.show-for-portrait,th.hide-for-landscape{display:table-cell !important}}.show-for-touch{display:none !important}.hide-for-touch{display:inherit !important}.touch .show-for-touch{display:inherit !important}.touch .hide-for-touch{display:none !important}table.hide-for-touch{display:table !important}.touch table.show-for-touch{display:table !important}thead.hide-for-touch{display:table-header-group !important}.touch thead.show-for-touch{display:table-header-group !important}tbody.hide-for-touch{display:table-row-group !important}.touch tbody.show-for-touch{display:table-row-group !important}tr.hide-for-touch{display:table-row !important}.touch tr.show-for-touch{display:table-row !important}td.hide-for-touch{display:table-cell !important}.touch td.show-for-touch{display:table-cell !important}th.hide-for-touch{display:table-cell !important}.touch th.show-for-touch{display:table-cell !important}.show-for-sr{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px}.show-on-focus{clip:rect(1px, 1px, 1px, 1px);height:1px;overflow:hidden;position:absolute !important;width:1px}.show-on-focus:focus,.show-on-focus:active{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.print-only{display:none !important}@media print{*{background:transparent !important;box-shadow:none !important;color:#000 !important;text-shadow:none !important}.show-for-print{display:block}.hide-for-print{display:none}table.show-for-print{display:table !important}thead.show-for-print{display:table-header-group !important}tbody.show-for-print{display:table-row-group !important}tr.show-for-print{display:table-row !important}td.show-for-print{display:table-cell !important}th.show-for-print{display:table-cell !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.hide-on-print{display:none !important}.print-only{display:block !important}.hide-for-print{display:none !important}.show-for-print{display:inherit !important}}@media print{.show-for-print{display:block}.hide-for-print{display:none}table.show-for-print{display:table !important}thead.show-for-print{display:table-header-group !important}tbody.show-for-print{display:table-row-group !important}tr.show-for-print{display:table-row !important}td.show-for-print{display:table-cell !important}th.show-for-print{display:table-cell !important}}@media not print{.show-for-print{display:none !important}} \ No newline at end of file diff --git a/cake-3.2/webroot/css/cake.css b/cake-3.2/webroot/css/cake.css new file mode 100644 index 000000000..bdeba0adb --- /dev/null +++ b/cake-3.2/webroot/css/cake.css @@ -0,0 +1,578 @@ +.disabled a, +a.disabled { + pointer-events: none; +} + +a:hover { + color: #15848F; +} + +a { + color: #1798A5; +} + +.side-nav li a:not(.button) { + color: #15848F; +} + +.side-nav li a:not(.button):hover { + color: #15848F; +} + +header { + background-color: #15848F; + color: #ffffff; + font-size: 30px; + height: 84px; + line-height: 64px; + padding: 16px 0px; + box-shadow: 0px 1px rgba(0, 0, 0, 0.24); +} + +header .header-title { + padding-left:80px +} + +legend { + color:#15848F; +} + +.row { + max-width: 80rem; +} + +.actions.columns { + margin-top:1rem; + border-left: 5px solid #15848F; + padding-left: 15px; + padding: 32px 20px; +} + +.actions.columns h3 { + color:#15848F; +} + +.index table { + margin-top: 2rem; + border: 0; + width: 100%; + table-layout: fixed; +} + +.related table { + border: 0; + width: 100%; + table-layout: fixed; +} + +.index table thead { + height: 3.5rem; +} + +.header-help { + float: right; + margin-right:2rem; + margin-top: -80px; + font-size:16px; +} + +.header-help span { + font-weight: normal; + text-align: center; + text-decoration: none; + line-height: 1; + white-space: nowrap; + display: inline-block; + padding: 0.25rem 0.5rem 0.375rem; + font-size: 0.8rem; + background-color: #0097a7; + color: #FFF; + border-radius: 1000px; +} + +.header-help a { + color: #fff; +} + +ul.pagination li a { + color: rgba(0, 0 ,0 , 0.54); +} + +ul.pagination li.active a { + background-color: #DCE47E; + color: #FFF; + font-weight: bold; + cursor: default; +} +ul.pagination .disabled:hover a { + background: none; +} + +.paginator { + text-align: center; +} + +.paginator ul.pagination li { + float: none; + display: inline-block; +} + +.paginator p { + text-align: right; + color: rgba(0, 0 ,0 , 0.54); +} + +.asc:after { + content: " \2193"; +} +.desc:after { + content: " \2191"; +} + +button { + background: #8D6E65; +} + +.form button:hover, .form button:focus { + background: #7A6058; + box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.26) !important; +} + +.form button[type="submit"] { + float: right; + text-transform: uppercase; + box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.26); +} + +.form .error-message { + display: block; + padding: 0.375rem 0.5625rem 0.5625rem; + margin-top: -1px; + margin-bottom: 1rem; + font-size: 0.75rem; + font-weight: normal; + font-style: italic; + color: rgba(0, 0, 0, 0.54); +} + +.required > label { + font-weight: bold; +} +.required > label:after { + content: ' *'; + color: #C3232D; +} + +select[multiple] { + min-height:150px; + background: none; +} +input[type=checkbox], +input[type=radio] { + margin-right: 0.5em; +} + +.date select, +.time select, +.datetime select { + display: inline; + width: auto; + margin-right: 10px; +} + +.error label, +.error label.error { + color: #C3232D; +} + +.view h2 { + color: #6F6F6F; +} + +.view .columns.strings { + border-radius: 3px; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.24); + margin-right:0.7rem; +} + +.view .numbers { + background-color: #B7E3EC; + color: #FFF; + border-radius: 3px; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.24); + margin-right: 0.7rem; +} + +.view .columns.dates { + border-radius: 3px; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.24); + margin-right:0.7rem; + background-color:#DCE47E; + color: #fff; +} + +.view .columns.booleans { + border-radius: 3px; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.24); + margin-right:0.7rem; + background-color: #8D6E65; + color: #fff; +} + +.view .strings p { + border-bottom: 1px solid #eee; +} +.view .numbers .subheader, .view .dates .subheader { + color:#747474; +} +.view .booleans .subheader { + color: #E9E9E9 +} + +.view .texts .columns { + margin-top:1.2rem; + border-bottom: 1px solid #eee; +} + +/** Notices and Errors **/ +.cake-error, +.cake-debug, +.notice, +p.error, +p.notice { + display: block; + clear: both; + background-repeat: repeat-x; + margin-bottom: 18px; + padding: 7px 14px; + border-radius: 3px; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.24); +} + +.cake-debug, +.notice, +p.notice { + color: #000000; + background: #ffcc00; +} + +.cake-error, +p.error { + color: #fff; + background: #C3232D; +} + +pre { + background: none repeat scroll 0% 0% #FFF; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.24); + margin: 15px 0px; + color: rgba(0, 0 ,0 , 0.74); + padding:5px; +} + +.cake-error .cake-stack-trace { + margin-top:10px; +} + +.cake-stack-trace code { + background: inherit; + border:0; +} + +.cake-code-dump .code-highlight { + display: block; + background-color: #FFC600; +} + +.cake-error a, +.cake-error a:hover { + color:#fff; + text-decoration: underline; +} + +.home header { + width: 100%; + height: 85%; + position: relative; + display: table; +} + +.home h1 { + font-family: "Gill Sans MT", Calibri, sans-serif; +} + +.home header .header-image { + display: table-cell; + vertical-align: middle; + text-align: center; +} + +.home header h1 { + color: #fff; +} + +#content { + padding-top: 2em; + margin: 0 auto; + width: 95%; +} + +.checks { + padding:30px; + color: #626262; + background-color: #B7E3EC; + border-radius: 3px; + box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.24); + margin-bottom: 2em; +} + +.checks h4 { + margin-bottom: 1.5rem; +} + +.checks hr { + border: 0; + height: 0; + border-top: 1px solid rgba(0, 0, 0, 0.1); + border-bottom: 1px solid rgba(255, 255, 255, 0.3); +} + +.home .checks.ctp-warning { + color: #fff; + background-color: #ff5555; +} + +.home .checks.url-rewriting { + background-color: #F0F0F0; + display: none; +} + +.checks .success, +.checks .problem { + margin-left: 10px; +} +.checks .success:before, +.checks .problem:before { + line-height: 0px; + font-size: 28px; + height: 12px; + width: 12px; + border-radius: 15px; + text-align: center; + vertical-align: middle; + display: inline-block; + position: relative; + left: -11px; +} + +.checks .success:before { + content: "✓"; + color: green; + margin-right: 9px; +} + +.checks .problem:before { + content: "✘"; + color: red; + margin-right: 9px; +} + +.top-bar.expanded .title-area { + background: #01545b; +} + +.top-bar.expanded, .top-bar,.top-bar-section ul li,.top-bar-section li:not(.has-form) a:not(.button) { + background: #116d76; +} + +.top-bar-section li:not(.has-form) a:not(.button):hover { + background-color: #308e97; + background: #308e97; +} + +.side-nav li.heading { + color: #1798A5; + font-size: 0.875rem; + font-weight: bold; + text-transform: uppercase; + padding: 0.4375rem 0.875rem; +} + +#actions-sidebar { + background: #fafafa; +} + +.index table { + margin-top: 0rem; + border: 0; +} + +table { + background: #fff; + margin-bottom: 1.25rem; + border: none; + table-layout: fixed; + width: 100%; +} + +table thead { + background: none; +} + +table tr { + border-bottom: 1px solid #ebebec; +} + +table thead tr { + border-bottom: 1px solid #1798A5; +} + +table tr th { + padding: 0.5625rem 0.625rem; + font-size: 0.875rem; + color: #1798A5; + text-align: left; + border-bottom: 2px solid #1798A5; +} + +table tr:nth-of-type(even) { + background: none; +} + +fieldset { + border: none; + padding: 1.25rem; + margin: 1.125rem 0; +} + +fieldset legend { + border-bottom: 2px solid #1798A5; + width: 100%; + line-height: 2rem; +} + +.form button[type="submit"] { + float: right; + text-transform: uppercase; + box-shadow: none; +} + +.form button:hover, .form button:focus { + background: #BE840B; + box-shadow: none; +} + +button { + background: #966600; +} + +div.message { + text-align: center; + cursor: pointer; + display: block; + font-weight: normal; + padding: 0 1.5rem 0 1.5rem; + transition: height 300ms ease-out 0s; + background-color: #a0d3e8; + color: #626262; + top: 15px; + right: 15px; + z-index: 999; + overflow: hidden; + height: 50px; + line-height: 2.5em; + box-radius: 5px; + +} + +div.message:before { + line-height: 0px; + font-size: 20px; + height: 12px; + width: 12px; + border-radius: 15px; + text-align: center; + vertical-align: middle; + display: inline-block; + position: relative; + left: -11px; + background-color: #FFF; + padding: 12px 14px 12px 10px; + content: "i"; + color: #a0d3e8; +} + +div.message.error { + background-color: #C3232D; + color: #FFF; +} + +div.message.error:before { + padding: 11px 16px 14px 7px; + color: #C3232D; + content: "x"; +} +div.message.hidden { + height: 0; +} + + +.vertical-table th { + padding: 0.5625rem 0.625rem; + font-size: 0.875rem; + color: #1798A5; + border: none; + text-align: left; +} + +.vertical-table { + vertical-align: middle; +} + +.vertical-table td { + text-align: right; +} + +.content { + padding: 2rem; +} + +/* Use 'one true layout' methods to get equal height columns */ +.container { + overflow: hidden; + min-height: 92%; /* full height almost always */ +} + +/* Force equal height by overflowing */ +.content, +#actions-sidebar { + margin-bottom: -99999px; + padding-bottom: 99999px; +} +@media(max-width: 640px) { + #actions-sidebar { + padding-bottom: 2rem; + margin-bottom: 0; + } +} + +.content h3 { + color: #be140b; + padding-bottom: 0.5rem; + margin-bottom: 20px; +} + +.content h4 { + color: #be140b; + padding-bottom: 0.5rem; + margin-bottom: 20px; + border-bottom: 2px solid #be140b; +} + +.content .related h4 { + color: #4d8f97; + padding-bottom: 0.5rem; + margin-top: 20px; + margin-bottom: 10px; + border-bottom: 0px; +} diff --git a/cake-3.2/webroot/favicon.ico b/cake-3.2/webroot/favicon.ico new file mode 100644 index 000000000..aaac9c853 Binary files /dev/null and b/cake-3.2/webroot/favicon.ico differ diff --git a/cake-3.2/webroot/img/cake.icon.png b/cake-3.2/webroot/img/cake.icon.png new file mode 100644 index 000000000..394fa42d5 Binary files /dev/null and b/cake-3.2/webroot/img/cake.icon.png differ diff --git a/cake-3.2/webroot/img/cake.power.gif b/cake-3.2/webroot/img/cake.power.gif new file mode 100644 index 000000000..8f8d570a2 Binary files /dev/null and b/cake-3.2/webroot/img/cake.power.gif differ diff --git a/cake-3.2/webroot/index.php b/cake-3.2/webroot/index.php new file mode 100644 index 000000000..19cef45a8 --- /dev/null +++ b/cake-3.2/webroot/index.php @@ -0,0 +1,39 @@ +dispatch( + Request::createFromGlobals(), + new Response() +); + +require $_SERVER['DOCUMENT_ROOT'].'/php-framework-benchmark/libs/output_data.php'; diff --git a/cake-3.2/webroot/js/empty b/cake-3.2/webroot/js/empty new file mode 100644 index 000000000..e69de29bb diff --git a/ci-3.0/.gitignore b/ci-3.0/.gitignore new file mode 100644 index 000000000..5982f9bad --- /dev/null +++ b/ci-3.0/.gitignore @@ -0,0 +1,27 @@ +.DS_Store + +application/cache/* +!application/cache/index.html +!application/cache/.htaccess + +application/logs/* +!application/logs/index.html +!application/logs/.htaccess + +user_guide_src/build/* +user_guide_src/cilexer/build/* +user_guide_src/cilexer/dist/* +user_guide_src/cilexer/pycilexer.egg-info/* +/vendor/ + +# IDE Files +#------------------------- +/nbproject/ +.idea/* + +## Sublime Text cache files +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache +*.sublime-workspace +*.sublime-project diff --git a/ci-3.0/_benchmark/hello_world.sh b/ci-3.0/_benchmark/hello_world.sh new file mode 100644 index 000000000..f0f049590 --- /dev/null +++ b/ci-3.0/_benchmark/hello_world.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +url="$base/$fw/index.php/hello/index" diff --git a/ci-3.0/_benchmark/setup.sh b/ci-3.0/_benchmark/setup.sh new file mode 100644 index 000000000..f5e7e3526 --- /dev/null +++ b/ci-3.0/_benchmark/setup.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +rm -rf user_guide diff --git a/codeigniter-3.0/application/cache/index.html b/ci-3.0/application/cache/index.html similarity index 100% rename from codeigniter-3.0/application/cache/index.html rename to ci-3.0/application/cache/index.html diff --git a/ci-3.0/application/config/autoload.php b/ci-3.0/application/config/autoload.php new file mode 100644 index 000000000..4bc6bf0ad --- /dev/null +++ b/ci-3.0/application/config/autoload.php @@ -0,0 +1,129 @@ + 'ua'); +*/ +$autoload['libraries'] = array(); + +/* +| ------------------------------------------------------------------- +| Auto-load Drivers +| ------------------------------------------------------------------- +| These classes are located in system/libraries/ or in your +| application/libraries/ directory, but are also placed inside their +| own subdirectory and they extend the CI_Driver_Library class. They +| offer multiple interchangeable driver options. +| +| Prototype: +| +| $autoload['drivers'] = array('cache'); +*/ +$autoload['drivers'] = array(); + +/* +| ------------------------------------------------------------------- +| Auto-load Helper Files +| ------------------------------------------------------------------- +| Prototype: +| +| $autoload['helper'] = array('url', 'file'); +*/ +$autoload['helper'] = array(); + +/* +| ------------------------------------------------------------------- +| Auto-load Config files +| ------------------------------------------------------------------- +| Prototype: +| +| $autoload['config'] = array('config1', 'config2'); +| +| NOTE: This item is intended for use ONLY if you have created custom +| config files. Otherwise, leave it blank. +| +*/ +$autoload['config'] = array(); + +/* +| ------------------------------------------------------------------- +| Auto-load Language files +| ------------------------------------------------------------------- +| Prototype: +| +| $autoload['language'] = array('lang1', 'lang2'); +| +| NOTE: Do not include the "_lang" part of your file. For example +| "codeigniter_lang.php" would be referenced as array('codeigniter'); +| +*/ +$autoload['language'] = array(); + +/* +| ------------------------------------------------------------------- +| Auto-load Models +| ------------------------------------------------------------------- +| Prototype: +| +| $autoload['model'] = array('first_model', 'second_model'); +| +| You can also supply an alternative model name to be assigned +| in the controller: +| +| $autoload['model'] = array('first_model' => 'first'); +*/ +$autoload['model'] = array(); diff --git a/ci-3.0/application/config/config.php b/ci-3.0/application/config/config.php new file mode 100644 index 000000000..479d591a4 --- /dev/null +++ b/ci-3.0/application/config/config.php @@ -0,0 +1,507 @@ +]+$/i +| +| DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!! +| +*/ +$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-'; + +/* +|-------------------------------------------------------------------------- +| Enable Query Strings +|-------------------------------------------------------------------------- +| +| By default CodeIgniter uses search-engine friendly segment based URLs: +| example.com/who/what/where/ +| +| By default CodeIgniter enables access to the $_GET array. If for some +| reason you would like to disable it, set 'allow_get_array' to FALSE. +| +| You can optionally enable standard query string based URLs: +| example.com?who=me&what=something&where=here +| +| Options are: TRUE or FALSE (boolean) +| +| The other items let you set the query string 'words' that will +| invoke your controllers and its functions: +| example.com/index.php?c=controller&m=function +| +| Please note that some of the helpers won't work as expected when +| this feature is enabled, since CodeIgniter is designed primarily to +| use segment based URLs. +| +*/ +$config['allow_get_array'] = TRUE; +$config['enable_query_strings'] = FALSE; +$config['controller_trigger'] = 'c'; +$config['function_trigger'] = 'm'; +$config['directory_trigger'] = 'd'; + +/* +|-------------------------------------------------------------------------- +| Error Logging Threshold +|-------------------------------------------------------------------------- +| +| You can enable error logging by setting a threshold over zero. The +| threshold determines what gets logged. Threshold options are: +| +| 0 = Disables logging, Error logging TURNED OFF +| 1 = Error Messages (including PHP errors) +| 2 = Debug Messages +| 3 = Informational Messages +| 4 = All Messages +| +| You can also pass an array with threshold levels to show individual error types +| +| array(2) = Debug Messages, without Error Messages +| +| For a live site you'll usually only enable Errors (1) to be logged otherwise +| your log files will fill up very fast. +| +*/ +$config['log_threshold'] = 0; + +/* +|-------------------------------------------------------------------------- +| Error Logging Directory Path +|-------------------------------------------------------------------------- +| +| Leave this BLANK unless you would like to set something other than the default +| application/logs/ directory. Use a full server path with trailing slash. +| +*/ +$config['log_path'] = ''; + +/* +|-------------------------------------------------------------------------- +| Log File Extension +|-------------------------------------------------------------------------- +| +| The default filename extension for log files. The default 'php' allows for +| protecting the log files via basic scripting, when they are to be stored +| under a publicly accessible directory. +| +| Note: Leaving it blank will default to 'php'. +| +*/ +$config['log_file_extension'] = ''; + +/* +|-------------------------------------------------------------------------- +| Log File Permissions +|-------------------------------------------------------------------------- +| +| The file system permissions to be applied on newly created log files. +| +| IMPORTANT: This MUST be an integer (no quotes) and you MUST use octal +| integer notation (i.e. 0700, 0644, etc.) +*/ +$config['log_file_permissions'] = 0644; + +/* +|-------------------------------------------------------------------------- +| Date Format for Logs +|-------------------------------------------------------------------------- +| +| Each item that is logged has an associated date. You can use PHP date +| codes to set your own date formatting +| +*/ +$config['log_date_format'] = 'Y-m-d H:i:s'; + +/* +|-------------------------------------------------------------------------- +| Error Views Directory Path +|-------------------------------------------------------------------------- +| +| Leave this BLANK unless you would like to set something other than the default +| application/views/errors/ directory. Use a full server path with trailing slash. +| +*/ +$config['error_views_path'] = ''; + +/* +|-------------------------------------------------------------------------- +| Cache Directory Path +|-------------------------------------------------------------------------- +| +| Leave this BLANK unless you would like to set something other than the default +| application/cache/ directory. Use a full server path with trailing slash. +| +*/ +$config['cache_path'] = ''; + +/* +|-------------------------------------------------------------------------- +| Cache Include Query String +|-------------------------------------------------------------------------- +| +| Whether to take the URL query string into consideration when generating +| output cache files. Valid options are: +| +| FALSE = Disabled +| TRUE = Enabled, take all query parameters into account. +| Please be aware that this may result in numerous cache +| files generated for the same page over and over again. +| array('q') = Enabled, but only take into account the specified list +| of query parameters. +| +*/ +$config['cache_query_string'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| Encryption Key +|-------------------------------------------------------------------------- +| +| If you use the Encryption class, you must set an encryption key. +| See the user guide for more info. +| +| http://codeigniter.com/user_guide/libraries/encryption.html +| +*/ +$config['encryption_key'] = ''; + +/* +|-------------------------------------------------------------------------- +| Session Variables +|-------------------------------------------------------------------------- +| +| 'sess_driver' +| +| The storage driver to use: files, database, redis, memcached +| +| 'sess_cookie_name' +| +| The session cookie name, must contain only [0-9a-z_-] characters +| +| 'sess_expiration' +| +| The number of SECONDS you want the session to last. +| Setting to 0 (zero) means expire when the browser is closed. +| +| 'sess_save_path' +| +| The location to save sessions to, driver dependent. +| +| For the 'files' driver, it's a path to a writable directory. +| WARNING: Only absolute paths are supported! +| +| For the 'database' driver, it's a table name. +| Please read up the manual for the format with other session drivers. +| +| IMPORTANT: You are REQUIRED to set a valid save path! +| +| 'sess_match_ip' +| +| Whether to match the user's IP address when reading the session data. +| +| WARNING: If you're using the database driver, don't forget to update +| your session table's PRIMARY KEY when changing this setting. +| +| 'sess_time_to_update' +| +| How many seconds between CI regenerating the session ID. +| +| 'sess_regenerate_destroy' +| +| Whether to destroy session data associated with the old session ID +| when auto-regenerating the session ID. When set to FALSE, the data +| will be later deleted by the garbage collector. +| +| Other session cookie settings are shared with the rest of the application, +| except for 'cookie_prefix' and 'cookie_httponly', which are ignored here. +| +*/ +$config['sess_driver'] = 'files'; +$config['sess_cookie_name'] = 'ci_session'; +$config['sess_expiration'] = 7200; +$config['sess_save_path'] = NULL; +$config['sess_match_ip'] = FALSE; +$config['sess_time_to_update'] = 300; +$config['sess_regenerate_destroy'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| Cookie Related Variables +|-------------------------------------------------------------------------- +| +| 'cookie_prefix' = Set a cookie name prefix if you need to avoid collisions +| 'cookie_domain' = Set to .your-domain.com for site-wide cookies +| 'cookie_path' = Typically will be a forward slash +| 'cookie_secure' = Cookie will only be set if a secure HTTPS connection exists. +| 'cookie_httponly' = Cookie will only be accessible via HTTP(S) (no javascript) +| +| Note: These settings (with the exception of 'cookie_prefix' and +| 'cookie_httponly') will also affect sessions. +| +*/ +$config['cookie_prefix'] = ''; +$config['cookie_domain'] = ''; +$config['cookie_path'] = '/'; +$config['cookie_secure'] = FALSE; +$config['cookie_httponly'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| Standardize newlines +|-------------------------------------------------------------------------- +| +| Determines whether to standardize newline characters in input data, +| meaning to replace \r\n, \r, \n occurrences with the PHP_EOL value. +| +| This is particularly useful for portability between UNIX-based OSes, +| (usually \n) and Windows (\r\n). +| +*/ +$config['standardize_newlines'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| Global XSS Filtering +|-------------------------------------------------------------------------- +| +| Determines whether the XSS filter is always active when GET, POST or +| COOKIE data is encountered +| +| WARNING: This feature is DEPRECATED and currently available only +| for backwards compatibility purposes! +| +*/ +$config['global_xss_filtering'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| Cross Site Request Forgery +|-------------------------------------------------------------------------- +| Enables a CSRF cookie token to be set. When set to TRUE, token will be +| checked on a submitted form. If you are accepting user data, it is strongly +| recommended CSRF protection be enabled. +| +| 'csrf_token_name' = The token name +| 'csrf_cookie_name' = The cookie name +| 'csrf_expire' = The number in seconds the token should expire. +| 'csrf_regenerate' = Regenerate token on every submission +| 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks +*/ +$config['csrf_protection'] = FALSE; +$config['csrf_token_name'] = 'csrf_test_name'; +$config['csrf_cookie_name'] = 'csrf_cookie_name'; +$config['csrf_expire'] = 7200; +$config['csrf_regenerate'] = TRUE; +$config['csrf_exclude_uris'] = array(); + +/* +|-------------------------------------------------------------------------- +| Output Compression +|-------------------------------------------------------------------------- +| +| Enables Gzip output compression for faster page loads. When enabled, +| the output class will test whether your server supports Gzip. +| Even if it does, however, not all browsers support compression +| so enable only if you are reasonably sure your visitors can handle it. +| +| Only used if zlib.output_compression is turned off in your php.ini. +| Please do not use it together with httpd-level output compression. +| +| VERY IMPORTANT: If you are getting a blank page when compression is enabled it +| means you are prematurely outputting something to your browser. It could +| even be a line of whitespace at the end of one of your scripts. For +| compression to work, nothing can be sent before the output buffer is called +| by the output class. Do not 'echo' any values with compression enabled. +| +*/ +$config['compress_output'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| Master Time Reference +|-------------------------------------------------------------------------- +| +| Options are 'local' or any PHP supported timezone. This preference tells +| the system whether to use your server's local time as the master 'now' +| reference, or convert it to the configured one timezone. See the 'date +| helper' page of the user guide for information regarding date handling. +| +*/ +$config['time_reference'] = 'local'; + +/* +|-------------------------------------------------------------------------- +| Rewrite PHP Short Tags +|-------------------------------------------------------------------------- +| +| If your PHP installation does not have short tag support enabled CI +| can rewrite the tags on-the-fly, enabling you to utilize that syntax +| in your view files. Options are TRUE or FALSE (boolean) +| +| Note: You need to have eval() enabled for this to work. +| +*/ +$config['rewrite_short_tags'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| Reverse Proxy IPs +|-------------------------------------------------------------------------- +| +| If your server is behind a reverse proxy, you must whitelist the proxy +| IP addresses from which CodeIgniter should trust headers such as +| HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP in order to properly identify +| the visitor's IP address. +| +| You can use both an array or a comma-separated list of proxy addresses, +| as well as specifying whole subnets. Here are a few examples: +| +| Comma-separated: '10.0.1.200,192.168.5.0/24' +| Array: array('10.0.1.200', '192.168.5.0/24') +*/ +$config['proxy_ips'] = ''; diff --git a/ci-3.0/application/config/constants.php b/ci-3.0/application/config/constants.php new file mode 100644 index 000000000..e8d2c00ea --- /dev/null +++ b/ci-3.0/application/config/constants.php @@ -0,0 +1,85 @@ +db->last_query() and profiling of DB queries. +| When you run a query, with this setting set to TRUE (default), +| CodeIgniter will store the SQL statement for debugging purposes. +| However, this may cause high memory usage, especially if you run +| a lot of SQL queries ... disable this to avoid that problem. +| +| The $active_group variable lets you choose which connection group to +| make active. By default there is only one group (the 'default' group). +| +| The $query_builder variables lets you determine whether or not to load +| the query builder class. +*/ +$active_group = 'default'; +$query_builder = TRUE; + +$db['default'] = array( + 'dsn' => '', + 'hostname' => 'localhost', + 'username' => '', + 'password' => '', + 'database' => '', + 'dbdriver' => 'mysqli', + 'dbprefix' => '', + 'pconnect' => FALSE, + 'db_debug' => (ENVIRONMENT !== 'production'), + 'cache_on' => FALSE, + 'cachedir' => '', + 'char_set' => 'utf8', + 'dbcollat' => 'utf8_general_ci', + 'swap_pre' => '', + 'encrypt' => FALSE, + 'compress' => FALSE, + 'stricton' => FALSE, + 'failover' => array(), + 'save_queries' => TRUE +); diff --git a/codeigniter-3.0/application/config/doctypes.php b/ci-3.0/application/config/doctypes.php similarity index 100% rename from codeigniter-3.0/application/config/doctypes.php rename to ci-3.0/application/config/doctypes.php diff --git a/ci-3.0/application/config/foreign_chars.php b/ci-3.0/application/config/foreign_chars.php new file mode 100644 index 000000000..ac406e3d4 --- /dev/null +++ b/ci-3.0/application/config/foreign_chars.php @@ -0,0 +1,103 @@ + 'ae', + '/ö|Å“/' => 'oe', + '/ü/' => 'ue', + '/Ä/' => 'Ae', + '/Ü/' => 'Ue', + '/Ö/' => 'Oe', + '/À|Ã|Â|Ã|Ä|Ã…|Ǻ|Ä€|Ä‚|Ä„|Ç|Α|Ά|Ả|Ạ|Ầ|Ẫ|Ẩ|Ậ|Ằ|Ắ|Ẵ|Ẳ|Ặ|Ð/' => 'A', + '/à|á|â|ã|Ã¥|Ç»|Ä|ă|Ä…|ÇŽ|ª|α|ά|ả|ạ|ầ|ấ|ẫ|ẩ|ậ|ằ|ắ|ẵ|ẳ|ặ|а/' => 'a', + '/Б/' => 'B', + '/б/' => 'b', + '/Ç|Ć|Ĉ|ÄŠ|ÄŒ/' => 'C', + '/ç|ć|ĉ|Ä‹|Ä/' => 'c', + '/Д/' => 'D', + '/д/' => 'd', + '/Ã|ÄŽ|Ä|Δ/' => 'Dj', + '/ð|Ä|Ä‘|δ/' => 'dj', + '/È|É|Ê|Ë|Ä’|Ä”|Ä–|Ę|Äš|Ε|Έ|Ẽ|Ẻ|Ẹ|Ề|Ế|Ễ|Ể|Ệ|Е|Э/' => 'E', + '/è|é|ê|ë|Ä“|Ä•|Ä—|Ä™|Ä›|έ|ε|ẽ|ẻ|ẹ|á»|ế|á»…|ể|ệ|е|Ñ/' => 'e', + '/Ф/' => 'F', + '/Ñ„/' => 'f', + '/Äœ|Äž|Ä |Ä¢|Γ|Г|Ò/' => 'G', + '/Ä|ÄŸ|Ä¡|Ä£|γ|г|Ò‘/' => 'g', + '/Ĥ|Ħ/' => 'H', + '/Ä¥|ħ/' => 'h', + '/ÃŒ|Ã|ÃŽ|Ã|Ĩ|Ī|Ĭ|Ç|Ä®|İ|Η|Ή|Ί|Ι|Ϊ|Ỉ|Ị|И|Ы/' => 'I', + '/ì|í|î|ï|Ä©|Ä«|Ä­|Ç|į|ı|η|ή|ί|ι|ÏŠ|ỉ|ị|и|Ñ‹|Ñ—/' => 'i', + '/Ä´/' => 'J', + '/ĵ/' => 'j', + '/Ķ|Κ|К/' => 'K', + '/Ä·|κ|к/' => 'k', + '/Ĺ|Ä»|Ľ|Ä¿|Å|Λ|Л/' => 'L', + '/ĺ|ļ|ľ|Å€|Å‚|λ|л/' => 'l', + '/М/' => 'M', + '/м/' => 'm', + '/Ñ|Ń|Å…|Ň|Î|Ð/' => 'N', + '/ñ|Å„|ņ|ň|ʼn|ν|н/' => 'n', + '/Ã’|Ó|Ô|Õ|ÅŒ|ÅŽ|Ç‘|Å|Æ |Ø|Ǿ|Ο|ÎŒ|Ω|Î|Ỏ|Ọ|á»’|á»|á»–|á»”|Ộ|Ờ|Ớ|á» |Ở|Ợ|О/' => 'O', + '/ò|ó|ô|õ|Å|Å|Ç’|Å‘|Æ¡|ø|Ç¿|º|ο|ÏŒ|ω|ÏŽ|á»|á»|ồ|ố|á»—|ổ|á»™|á»|á»›|ỡ|ở|ợ|о/' => 'o', + '/П/' => 'P', + '/п/' => 'p', + '/Å”|Å–|Ř|Ρ|Р/' => 'R', + '/Å•|Å—|Å™|Ï|Ñ€/' => 'r', + '/Åš|Åœ|Åž|Ș|Å |Σ|С/' => 'S', + '/Å›|Å|ÅŸ|È™|Å¡|Å¿|σ|Ï‚|Ñ/' => 's', + '/Èš|Å¢|Ť|Ŧ|Ï„|Т/' => 'T', + '/È›|Å£|Å¥|ŧ|Ñ‚/' => 't', + '/Þ|þ/' => 'th', + '/Ù|Ú|Û|Ũ|Ū|Ŭ|Å®|Ű|Ų|Ư|Ç“|Ç•|Ç—|Ç™|Ç›|Ũ|Ủ|Ụ|Ừ|Ứ|á»®|Ử|á»°|У/' => 'U', + '/ù|ú|û|Å©|Å«|Å­|ů|ű|ų|ư|Ç”|Ç–|ǘ|Çš|Çœ|Ï…|Ï|Ï‹|á»§|ụ|ừ|ứ|ữ|á»­|á»±|у/' => 'u', + '/Ã|Ÿ|Ŷ|Î¥|ÎŽ|Ϋ|Ỳ|Ỹ|á»¶|á»´|Й/' => 'Y', + '/ý|ÿ|Å·|ỳ|ỹ|á»·|ỵ|й/' => 'y', + '/Ð’/' => 'V', + '/в/' => 'v', + '/Å´/' => 'W', + '/ŵ/' => 'w', + '/Ź|Å»|Ž|Ζ|З/' => 'Z', + '/ź|ż|ž|ζ|з/' => 'z', + '/Æ|Ǽ/' => 'AE', + '/ß/' => 'ss', + '/IJ/' => 'IJ', + '/ij/' => 'ij', + '/Å’/' => 'OE', + '/Æ’/' => 'f', + '/ξ/' => 'ks', + '/Ï€/' => 'p', + '/β/' => 'v', + '/μ/' => 'm', + '/ψ/' => 'ps', + '/Ð/' => 'Yo', + '/Ñ‘/' => 'yo', + '/Є/' => 'Ye', + '/Ñ”/' => 'ye', + '/Ї/' => 'Yi', + '/Ж/' => 'Zh', + '/ж/' => 'zh', + '/Ð¥/' => 'Kh', + '/Ñ…/' => 'kh', + '/Ц/' => 'Ts', + '/ц/' => 'ts', + '/Ч/' => 'Ch', + '/ч/' => 'ch', + '/Ш/' => 'Sh', + '/ш/' => 'sh', + '/Щ/' => 'Shch', + '/щ/' => 'shch', + '/Ъ|ÑŠ|Ь|ÑŒ/' => '', + '/Ю/' => 'Yu', + '/ÑŽ/' => 'yu', + '/Я/' => 'Ya', + '/Ñ/' => 'ya' +); diff --git a/codeigniter-3.0/application/config/hooks.php b/ci-3.0/application/config/hooks.php similarity index 100% rename from codeigniter-3.0/application/config/hooks.php rename to ci-3.0/application/config/hooks.php diff --git a/codeigniter-3.0/application/config/index.html b/ci-3.0/application/config/index.html similarity index 100% rename from codeigniter-3.0/application/config/index.html rename to ci-3.0/application/config/index.html diff --git a/codeigniter-3.0/application/config/memcached.php b/ci-3.0/application/config/memcached.php similarity index 100% rename from codeigniter-3.0/application/config/memcached.php rename to ci-3.0/application/config/memcached.php diff --git a/ci-3.0/application/config/migration.php b/ci-3.0/application/config/migration.php new file mode 100644 index 000000000..4b585a65c --- /dev/null +++ b/ci-3.0/application/config/migration.php @@ -0,0 +1,84 @@ +migration->current() this is the version that schema will +| be upgraded / downgraded to. +| +*/ +$config['migration_version'] = 0; + +/* +|-------------------------------------------------------------------------- +| Migrations Path +|-------------------------------------------------------------------------- +| +| Path to your migrations folder. +| Typically, it will be within your application path. +| Also, writing permission is required within the migrations path. +| +*/ +$config['migration_path'] = APPPATH.'migrations/'; diff --git a/ci-3.0/application/config/mimes.php b/ci-3.0/application/config/mimes.php new file mode 100644 index 000000000..1f591ba6b --- /dev/null +++ b/ci-3.0/application/config/mimes.php @@ -0,0 +1,158 @@ + array('application/mac-binhex40', 'application/mac-binhex', 'application/x-binhex40', 'application/x-mac-binhex40'), + 'cpt' => 'application/mac-compactpro', + 'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel', 'text/plain'), + 'bin' => array('application/macbinary', 'application/mac-binary', 'application/octet-stream', 'application/x-binary', 'application/x-macbinary'), + 'dms' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'exe' => array('application/octet-stream', 'application/x-msdownload'), + 'class' => 'application/octet-stream', + 'psd' => array('application/x-photoshop', 'image/vnd.adobe.photoshop'), + 'so' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'dll' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => array('application/pdf', 'application/force-download', 'application/x-download', 'binary/octet-stream'), + 'ai' => array('application/pdf', 'application/postscript'), + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => array('application/vnd.ms-excel', 'application/msexcel', 'application/x-msexcel', 'application/x-ms-excel', 'application/x-excel', 'application/x-dos_ms_excel', 'application/xls', 'application/x-xls', 'application/excel', 'application/download', 'application/vnd.ms-office', 'application/msword'), + 'ppt' => array('application/powerpoint', 'application/vnd.ms-powerpoint', 'application/vnd.ms-office', 'application/msword'), + 'pptx' => array('application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/x-zip', 'application/zip'), + 'wbxml' => 'application/wbxml', + 'wmlc' => 'application/wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'gz' => 'application/x-gzip', + 'gzip' => 'application/x-gzip', + 'php' => array('application/x-httpd-php', 'application/php', 'application/x-php', 'text/php', 'text/x-php', 'application/x-httpd-php-source'), + 'php4' => 'application/x-httpd-php', + 'php3' => 'application/x-httpd-php', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'js' => array('application/x-javascript', 'text/plain'), + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => array('application/x-tar', 'application/x-gzip-compressed'), + 'z' => 'application/x-compress', + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed', 'application/s-compressed', 'multipart/x-zip'), + 'rar' => array('application/x-rar', 'application/rar', 'application/x-rar-compressed'), + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'), + 'aif' => array('audio/x-aiff', 'audio/aiff'), + 'aiff' => array('audio/x-aiff', 'audio/aiff'), + 'aifc' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'rv' => 'video/vnd.rn-realvideo', + 'wav' => array('audio/x-wav', 'audio/wave', 'audio/wav'), + 'bmp' => array('image/bmp', 'image/x-bmp', 'image/x-bitmap', 'image/x-xbitmap', 'image/x-win-bitmap', 'image/x-windows-bmp', 'image/ms-bmp', 'image/x-ms-bmp', 'application/bmp', 'application/x-bmp', 'application/x-win-bitmap'), + 'gif' => 'image/gif', + 'jpeg' => array('image/jpeg', 'image/pjpeg'), + 'jpg' => array('image/jpeg', 'image/pjpeg'), + 'jpe' => array('image/jpeg', 'image/pjpeg'), + 'png' => array('image/png', 'image/x-png'), + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'css' => array('text/css', 'text/plain'), + 'html' => array('text/html', 'text/plain'), + 'htm' => array('text/html', 'text/plain'), + 'shtml' => array('text/html', 'text/plain'), + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'log' => array('text/plain', 'text/x-log'), + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'xml' => array('application/xml', 'text/xml', 'text/plain'), + 'xsl' => array('application/xml', 'text/xsl', 'text/xml'), + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'avi' => array('video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'), + 'movie' => 'video/x-sgi-movie', + 'doc' => array('application/msword', 'application/vnd.ms-office'), + 'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword', 'application/x-zip'), + 'dot' => array('application/msword', 'application/vnd.ms-office'), + 'dotx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword'), + 'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip', 'application/vnd.ms-excel', 'application/msword', 'application/x-zip'), + 'word' => array('application/msword', 'application/octet-stream'), + 'xl' => 'application/excel', + 'eml' => 'message/rfc822', + 'json' => array('application/json', 'text/json'), + 'pem' => array('application/x-x509-user-cert', 'application/x-pem-file', 'application/octet-stream'), + 'p10' => array('application/x-pkcs10', 'application/pkcs10'), + 'p12' => 'application/x-pkcs12', + 'p7a' => 'application/x-pkcs7-signature', + 'p7c' => array('application/pkcs7-mime', 'application/x-pkcs7-mime'), + 'p7m' => array('application/pkcs7-mime', 'application/x-pkcs7-mime'), + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'crt' => array('application/x-x509-ca-cert', 'application/x-x509-user-cert', 'application/pkix-cert'), + 'crl' => array('application/pkix-crl', 'application/pkcs-crl'), + 'der' => 'application/x-x509-ca-cert', + 'kdb' => 'application/octet-stream', + 'pgp' => 'application/pgp', + 'gpg' => 'application/gpg-keys', + 'sst' => 'application/octet-stream', + 'csr' => 'application/octet-stream', + 'rsa' => 'application/x-pkcs7', + 'cer' => array('application/pkix-cert', 'application/x-x509-ca-cert'), + '3g2' => 'video/3gpp2', + '3gp' => array('video/3gp', 'video/3gpp'), + 'mp4' => 'video/mp4', + 'm4a' => 'audio/x-m4a', + 'f4v' => 'video/mp4', + 'webm' => 'video/webm', + 'aac' => 'audio/x-acc', + 'm4u' => 'application/vnd.mpegurl', + 'm3u' => 'text/plain', + 'xspf' => 'application/xspf+xml', + 'vlc' => 'application/videolan', + 'wmv' => array('video/x-ms-wmv', 'video/x-ms-asf'), + 'au' => 'audio/x-au', + 'ac3' => 'audio/ac3', + 'flac' => 'audio/x-flac', + 'ogg' => 'audio/ogg', + 'kmz' => array('application/vnd.google-earth.kmz', 'application/zip', 'application/x-zip'), + 'kml' => array('application/vnd.google-earth.kml+xml', 'application/xml', 'text/xml'), + 'ics' => 'text/calendar', + 'ical' => 'text/calendar', + 'zsh' => 'text/x-scriptzsh', + '7zip' => array('application/x-compressed', 'application/x-zip-compressed', 'application/zip', 'multipart/x-zip'), + 'cdr' => array('application/cdr', 'application/coreldraw', 'application/x-cdr', 'application/x-coreldraw', 'image/cdr', 'image/x-cdr', 'zz-application/zz-winassoc-cdr'), + 'wma' => array('audio/x-ms-wma', 'video/x-ms-asf'), + 'jar' => array('application/java-archive', 'application/x-java-application', 'application/x-jar', 'application/x-compressed'), + 'svg' => array('image/svg+xml', 'application/xml', 'text/xml'), + 'vcf' => 'text/x-vcard', + 'srt' => array('text/srt', 'text/plain'), + 'vtt' => array('text/vtt', 'text/plain'), + 'ico' => array('image/x-icon', 'image/x-ico', 'image/vnd.microsoft.icon') +); diff --git a/codeigniter-3.0/application/config/profiler.php b/ci-3.0/application/config/profiler.php similarity index 100% rename from codeigniter-3.0/application/config/profiler.php rename to ci-3.0/application/config/profiler.php diff --git a/codeigniter-3.0/application/config/routes.php b/ci-3.0/application/config/routes.php similarity index 100% rename from codeigniter-3.0/application/config/routes.php rename to ci-3.0/application/config/routes.php diff --git a/ci-3.0/application/config/smileys.php b/ci-3.0/application/config/smileys.php new file mode 100644 index 000000000..1eeba4776 --- /dev/null +++ b/ci-3.0/application/config/smileys.php @@ -0,0 +1,64 @@ + array('grin.gif', '19', '19', 'grin'), + ':lol:' => array('lol.gif', '19', '19', 'LOL'), + ':cheese:' => array('cheese.gif', '19', '19', 'cheese'), + ':)' => array('smile.gif', '19', '19', 'smile'), + ';-)' => array('wink.gif', '19', '19', 'wink'), + ';)' => array('wink.gif', '19', '19', 'wink'), + ':smirk:' => array('smirk.gif', '19', '19', 'smirk'), + ':roll:' => array('rolleyes.gif', '19', '19', 'rolleyes'), + ':-S' => array('confused.gif', '19', '19', 'confused'), + ':wow:' => array('surprise.gif', '19', '19', 'surprised'), + ':bug:' => array('bigsurprise.gif', '19', '19', 'big surprise'), + ':-P' => array('tongue_laugh.gif', '19', '19', 'tongue laugh'), + '%-P' => array('tongue_rolleye.gif', '19', '19', 'tongue rolleye'), + ';-P' => array('tongue_wink.gif', '19', '19', 'tongue wink'), + ':P' => array('raspberry.gif', '19', '19', 'raspberry'), + ':blank:' => array('blank.gif', '19', '19', 'blank stare'), + ':long:' => array('longface.gif', '19', '19', 'long face'), + ':ohh:' => array('ohh.gif', '19', '19', 'ohh'), + ':grrr:' => array('grrr.gif', '19', '19', 'grrr'), + ':gulp:' => array('gulp.gif', '19', '19', 'gulp'), + '8-/' => array('ohoh.gif', '19', '19', 'oh oh'), + ':down:' => array('downer.gif', '19', '19', 'downer'), + ':red:' => array('embarrassed.gif', '19', '19', 'red face'), + ':sick:' => array('sick.gif', '19', '19', 'sick'), + ':shut:' => array('shuteye.gif', '19', '19', 'shut eye'), + ':-/' => array('hmm.gif', '19', '19', 'hmmm'), + '>:(' => array('mad.gif', '19', '19', 'mad'), + ':mad:' => array('mad.gif', '19', '19', 'mad'), + '>:-(' => array('angry.gif', '19', '19', 'angry'), + ':angry:' => array('angry.gif', '19', '19', 'angry'), + ':zip:' => array('zip.gif', '19', '19', 'zipper'), + ':kiss:' => array('kiss.gif', '19', '19', 'kiss'), + ':ahhh:' => array('shock.gif', '19', '19', 'shock'), + ':coolsmile:' => array('shade_smile.gif', '19', '19', 'cool smile'), + ':coolsmirk:' => array('shade_smirk.gif', '19', '19', 'cool smirk'), + ':coolgrin:' => array('shade_grin.gif', '19', '19', 'cool grin'), + ':coolhmm:' => array('shade_hmm.gif', '19', '19', 'cool hmm'), + ':coolmad:' => array('shade_mad.gif', '19', '19', 'cool mad'), + ':coolcheese:' => array('shade_cheese.gif', '19', '19', 'cool cheese'), + ':vampire:' => array('vampire.gif', '19', '19', 'vampire'), + ':snake:' => array('snake.gif', '19', '19', 'snake'), + ':exclaim:' => array('exclaim.gif', '19', '19', 'exclaim'), + ':question:' => array('question.gif', '19', '19', 'question') + +); diff --git a/ci-3.0/application/config/user_agents.php b/ci-3.0/application/config/user_agents.php new file mode 100644 index 000000000..1129dbacd --- /dev/null +++ b/ci-3.0/application/config/user_agents.php @@ -0,0 +1,211 @@ + 'Windows 10', + 'windows nt 6.3' => 'Windows 8.1', + 'windows nt 6.2' => 'Windows 8', + 'windows nt 6.1' => 'Windows 7', + 'windows nt 6.0' => 'Windows Vista', + 'windows nt 5.2' => 'Windows 2003', + 'windows nt 5.1' => 'Windows XP', + 'windows nt 5.0' => 'Windows 2000', + 'windows nt 4.0' => 'Windows NT 4.0', + 'winnt4.0' => 'Windows NT 4.0', + 'winnt 4.0' => 'Windows NT', + 'winnt' => 'Windows NT', + 'windows 98' => 'Windows 98', + 'win98' => 'Windows 98', + 'windows 95' => 'Windows 95', + 'win95' => 'Windows 95', + 'windows phone' => 'Windows Phone', + 'windows' => 'Unknown Windows OS', + 'android' => 'Android', + 'blackberry' => 'BlackBerry', + 'iphone' => 'iOS', + 'ipad' => 'iOS', + 'ipod' => 'iOS', + 'os x' => 'Mac OS X', + 'ppc mac' => 'Power PC Mac', + 'freebsd' => 'FreeBSD', + 'ppc' => 'Macintosh', + 'linux' => 'Linux', + 'debian' => 'Debian', + 'sunos' => 'Sun Solaris', + 'beos' => 'BeOS', + 'apachebench' => 'ApacheBench', + 'aix' => 'AIX', + 'irix' => 'Irix', + 'osf' => 'DEC OSF', + 'hp-ux' => 'HP-UX', + 'netbsd' => 'NetBSD', + 'bsdi' => 'BSDi', + 'openbsd' => 'OpenBSD', + 'gnu' => 'GNU/Linux', + 'unix' => 'Unknown Unix OS', + 'symbian' => 'Symbian OS' +); + + +// The order of this array should NOT be changed. Many browsers return +// multiple browser types so we want to identify the sub-type first. +$browsers = array( + 'OPR' => 'Opera', + 'Flock' => 'Flock', + 'Edge' => 'Spartan', + 'Chrome' => 'Chrome', + // Opera 10+ always reports Opera/9.80 and appends Version/ to the user agent string + 'Opera.*?Version' => 'Opera', + 'Opera' => 'Opera', + 'MSIE' => 'Internet Explorer', + 'Internet Explorer' => 'Internet Explorer', + 'Trident.* rv' => 'Internet Explorer', + 'Shiira' => 'Shiira', + 'Firefox' => 'Firefox', + 'Chimera' => 'Chimera', + 'Phoenix' => 'Phoenix', + 'Firebird' => 'Firebird', + 'Camino' => 'Camino', + 'Netscape' => 'Netscape', + 'OmniWeb' => 'OmniWeb', + 'Safari' => 'Safari', + 'Mozilla' => 'Mozilla', + 'Konqueror' => 'Konqueror', + 'icab' => 'iCab', + 'Lynx' => 'Lynx', + 'Links' => 'Links', + 'hotjava' => 'HotJava', + 'amaya' => 'Amaya', + 'IBrowse' => 'IBrowse', + 'Maxthon' => 'Maxthon', + 'Ubuntu' => 'Ubuntu Web Browser' +); + +$mobiles = array( + // legacy array, old values commented out + 'mobileexplorer' => 'Mobile Explorer', +// 'openwave' => 'Open Wave', +// 'opera mini' => 'Opera Mini', +// 'operamini' => 'Opera Mini', +// 'elaine' => 'Palm', + 'palmsource' => 'Palm', +// 'digital paths' => 'Palm', +// 'avantgo' => 'Avantgo', +// 'xiino' => 'Xiino', + 'palmscape' => 'Palmscape', +// 'nokia' => 'Nokia', +// 'ericsson' => 'Ericsson', +// 'blackberry' => 'BlackBerry', +// 'motorola' => 'Motorola' + + // Phones and Manufacturers + 'motorola' => 'Motorola', + 'nokia' => 'Nokia', + 'palm' => 'Palm', + 'iphone' => 'Apple iPhone', + 'ipad' => 'iPad', + 'ipod' => 'Apple iPod Touch', + 'sony' => 'Sony Ericsson', + 'ericsson' => 'Sony Ericsson', + 'blackberry' => 'BlackBerry', + 'cocoon' => 'O2 Cocoon', + 'blazer' => 'Treo', + 'lg' => 'LG', + 'amoi' => 'Amoi', + 'xda' => 'XDA', + 'mda' => 'MDA', + 'vario' => 'Vario', + 'htc' => 'HTC', + 'samsung' => 'Samsung', + 'sharp' => 'Sharp', + 'sie-' => 'Siemens', + 'alcatel' => 'Alcatel', + 'benq' => 'BenQ', + 'ipaq' => 'HP iPaq', + 'mot-' => 'Motorola', + 'playstation portable' => 'PlayStation Portable', + 'playstation 3' => 'PlayStation 3', + 'playstation vita' => 'PlayStation Vita', + 'hiptop' => 'Danger Hiptop', + 'nec-' => 'NEC', + 'panasonic' => 'Panasonic', + 'philips' => 'Philips', + 'sagem' => 'Sagem', + 'sanyo' => 'Sanyo', + 'spv' => 'SPV', + 'zte' => 'ZTE', + 'sendo' => 'Sendo', + 'nintendo dsi' => 'Nintendo DSi', + 'nintendo ds' => 'Nintendo DS', + 'nintendo 3ds' => 'Nintendo 3DS', + 'wii' => 'Nintendo Wii', + 'open web' => 'Open Web', + 'openweb' => 'OpenWeb', + + // Operating Systems + 'android' => 'Android', + 'symbian' => 'Symbian', + 'SymbianOS' => 'SymbianOS', + 'elaine' => 'Palm', + 'series60' => 'Symbian S60', + 'windows ce' => 'Windows CE', + + // Browsers + 'obigo' => 'Obigo', + 'netfront' => 'Netfront Browser', + 'openwave' => 'Openwave Browser', + 'mobilexplorer' => 'Mobile Explorer', + 'operamini' => 'Opera Mini', + 'opera mini' => 'Opera Mini', + 'opera mobi' => 'Opera Mobile', + 'fennec' => 'Firefox Mobile', + + // Other + 'digital paths' => 'Digital Paths', + 'avantgo' => 'AvantGo', + 'xiino' => 'Xiino', + 'novarra' => 'Novarra Transcoder', + 'vodafone' => 'Vodafone', + 'docomo' => 'NTT DoCoMo', + 'o2' => 'O2', + + // Fallback + 'mobile' => 'Generic Mobile', + 'wireless' => 'Generic Mobile', + 'j2me' => 'Generic Mobile', + 'midp' => 'Generic Mobile', + 'cldc' => 'Generic Mobile', + 'up.link' => 'Generic Mobile', + 'up.browser' => 'Generic Mobile', + 'smartphone' => 'Generic Mobile', + 'cellphone' => 'Generic Mobile' +); + +// There are hundreds of bots but these are the most common. +$robots = array( + 'googlebot' => 'Googlebot', + 'msnbot' => 'MSNBot', + 'baiduspider' => 'Baiduspider', + 'bingbot' => 'Bing', + 'slurp' => 'Inktomi Slurp', + 'yahoo' => 'Yahoo', + 'ask jeeves' => 'Ask Jeeves', + 'fastcrawler' => 'FastCrawler', + 'infoseek' => 'InfoSeek Robot 1.0', + 'lycos' => 'Lycos', + 'yandex' => 'YandexBot', + 'mediapartners-google' => 'MediaPartners Google', + 'CRAZYWEBCRAWLER' => 'Crazy Webcrawler', + 'adsbot-google' => 'AdsBot Google', + 'feedfetcher-google' => 'Feedfetcher Google', + 'curious george' => 'Curious George' +); diff --git a/codeigniter-3.0/application/controllers/Hello.php b/ci-3.0/application/controllers/Hello.php similarity index 100% rename from codeigniter-3.0/application/controllers/Hello.php rename to ci-3.0/application/controllers/Hello.php diff --git a/codeigniter-3.0/application/controllers/Welcome.php b/ci-3.0/application/controllers/Welcome.php similarity index 100% rename from codeigniter-3.0/application/controllers/Welcome.php rename to ci-3.0/application/controllers/Welcome.php diff --git a/codeigniter-3.0/application/controllers/index.html b/ci-3.0/application/controllers/index.html similarity index 100% rename from codeigniter-3.0/application/controllers/index.html rename to ci-3.0/application/controllers/index.html diff --git a/codeigniter-3.0/application/core/index.html b/ci-3.0/application/core/index.html similarity index 100% rename from codeigniter-3.0/application/core/index.html rename to ci-3.0/application/core/index.html diff --git a/codeigniter-3.0/application/helpers/index.html b/ci-3.0/application/helpers/index.html similarity index 100% rename from codeigniter-3.0/application/helpers/index.html rename to ci-3.0/application/helpers/index.html diff --git a/codeigniter-3.0/application/hooks/index.html b/ci-3.0/application/hooks/index.html similarity index 100% rename from codeigniter-3.0/application/hooks/index.html rename to ci-3.0/application/hooks/index.html diff --git a/codeigniter-3.0/application/index.html b/ci-3.0/application/index.html similarity index 100% rename from codeigniter-3.0/application/index.html rename to ci-3.0/application/index.html diff --git a/codeigniter-3.0/application/language/english/index.html b/ci-3.0/application/language/english/index.html similarity index 100% rename from codeigniter-3.0/application/language/english/index.html rename to ci-3.0/application/language/english/index.html diff --git a/codeigniter-3.0/application/language/index.html b/ci-3.0/application/language/index.html similarity index 100% rename from codeigniter-3.0/application/language/index.html rename to ci-3.0/application/language/index.html diff --git a/codeigniter-3.0/application/libraries/index.html b/ci-3.0/application/libraries/index.html similarity index 100% rename from codeigniter-3.0/application/libraries/index.html rename to ci-3.0/application/libraries/index.html diff --git a/codeigniter-3.0/application/logs/index.html b/ci-3.0/application/logs/index.html similarity index 100% rename from codeigniter-3.0/application/logs/index.html rename to ci-3.0/application/logs/index.html diff --git a/codeigniter-3.0/application/models/index.html b/ci-3.0/application/models/index.html similarity index 100% rename from codeigniter-3.0/application/models/index.html rename to ci-3.0/application/models/index.html diff --git a/codeigniter-3.0/application/third_party/index.html b/ci-3.0/application/third_party/index.html similarity index 100% rename from codeigniter-3.0/application/third_party/index.html rename to ci-3.0/application/third_party/index.html diff --git a/codeigniter-3.0/application/views/errors/cli/error_404.php b/ci-3.0/application/views/errors/cli/error_404.php similarity index 100% rename from codeigniter-3.0/application/views/errors/cli/error_404.php rename to ci-3.0/application/views/errors/cli/error_404.php diff --git a/codeigniter-3.0/application/views/errors/cli/error_db.php b/ci-3.0/application/views/errors/cli/error_db.php similarity index 100% rename from codeigniter-3.0/application/views/errors/cli/error_db.php rename to ci-3.0/application/views/errors/cli/error_db.php diff --git a/ci-3.0/application/views/errors/cli/error_exception.php b/ci-3.0/application/views/errors/cli/error_exception.php new file mode 100644 index 000000000..efa6a66d1 --- /dev/null +++ b/ci-3.0/application/views/errors/cli/error_exception.php @@ -0,0 +1,21 @@ + + +An uncaught Exception was encountered + +Type: +Message: +Filename: getFile(), "\n"; ?> +Line Number: getLine(); ?> + + + +Backtrace: +getTrace() as $error): ?> + + File: + Line: + Function: + + + + diff --git a/codeigniter-3.0/application/views/errors/cli/error_general.php b/ci-3.0/application/views/errors/cli/error_general.php similarity index 100% rename from codeigniter-3.0/application/views/errors/cli/error_general.php rename to ci-3.0/application/views/errors/cli/error_general.php diff --git a/ci-3.0/application/views/errors/cli/error_php.php b/ci-3.0/application/views/errors/cli/error_php.php new file mode 100644 index 000000000..8a24b6491 --- /dev/null +++ b/ci-3.0/application/views/errors/cli/error_php.php @@ -0,0 +1,21 @@ + + +A PHP Error was encountered + +Severity: +Message: +Filename: +Line Number: + + + +Backtrace: + + + File: + Line: + Function: + + + + diff --git a/codeigniter-3.0/application/views/errors/cli/index.html b/ci-3.0/application/views/errors/cli/index.html similarity index 100% rename from codeigniter-3.0/application/views/errors/cli/index.html rename to ci-3.0/application/views/errors/cli/index.html diff --git a/codeigniter-3.0/application/views/errors/html/error_404.php b/ci-3.0/application/views/errors/html/error_404.php similarity index 100% rename from codeigniter-3.0/application/views/errors/html/error_404.php rename to ci-3.0/application/views/errors/html/error_404.php diff --git a/codeigniter-3.0/application/views/errors/html/error_db.php b/ci-3.0/application/views/errors/html/error_db.php similarity index 100% rename from codeigniter-3.0/application/views/errors/html/error_db.php rename to ci-3.0/application/views/errors/html/error_db.php diff --git a/codeigniter-3.0/application/views/errors/html/error_exception.php b/ci-3.0/application/views/errors/html/error_exception.php similarity index 100% rename from codeigniter-3.0/application/views/errors/html/error_exception.php rename to ci-3.0/application/views/errors/html/error_exception.php diff --git a/codeigniter-3.0/application/views/errors/html/error_general.php b/ci-3.0/application/views/errors/html/error_general.php similarity index 100% rename from codeigniter-3.0/application/views/errors/html/error_general.php rename to ci-3.0/application/views/errors/html/error_general.php diff --git a/codeigniter-3.0/application/views/errors/html/error_php.php b/ci-3.0/application/views/errors/html/error_php.php similarity index 100% rename from codeigniter-3.0/application/views/errors/html/error_php.php rename to ci-3.0/application/views/errors/html/error_php.php diff --git a/codeigniter-3.0/application/views/errors/html/index.html b/ci-3.0/application/views/errors/html/index.html similarity index 100% rename from codeigniter-3.0/application/views/errors/html/index.html rename to ci-3.0/application/views/errors/html/index.html diff --git a/codeigniter-3.0/application/views/errors/index.html b/ci-3.0/application/views/errors/index.html similarity index 100% rename from codeigniter-3.0/application/views/errors/index.html rename to ci-3.0/application/views/errors/index.html diff --git a/codeigniter-3.0/application/views/index.html b/ci-3.0/application/views/index.html similarity index 100% rename from codeigniter-3.0/application/views/index.html rename to ci-3.0/application/views/index.html diff --git a/codeigniter-3.0/application/views/welcome_message.php b/ci-3.0/application/views/welcome_message.php similarity index 100% rename from codeigniter-3.0/application/views/welcome_message.php rename to ci-3.0/application/views/welcome_message.php diff --git a/ci-3.0/composer.json b/ci-3.0/composer.json new file mode 100644 index 000000000..0653a7885 --- /dev/null +++ b/ci-3.0/composer.json @@ -0,0 +1,19 @@ +{ + "description": "The CodeIgniter framework", + "name": "codeigniter/framework", + "type": "project", + "homepage": "http://codeigniter.com", + "license": "MIT", + "support": { + "forum": "http://forum.codeigniter.com/", + "wiki": "https://github.com/bcit-ci/CodeIgniter/wiki", + "irc": "irc://irc.freenode.net/codeigniter", + "source": "https://github.com/bcit-ci/CodeIgniter" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "mikey179/vfsStream": "1.1.*" + } +} \ No newline at end of file diff --git a/ci-3.0/composer.lock b/ci-3.0/composer.lock new file mode 100644 index 000000000..670da8860 --- /dev/null +++ b/ci-3.0/composer.lock @@ -0,0 +1,51 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "c7d2339afdb28c9c33e2500719d92a8f", + "content-hash": "163eb4764cfceda4201e2d993dd1e79d", + "packages": [], + "packages-dev": [ + { + "name": "mikey179/vfsStream", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/mikey179/vfsStream.git", + "reference": "fc0fe8f4d0b527254a2dc45f0c265567c881d07e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mikey179/vfsStream/zipball/fc0fe8f4d0b527254a2dc45f0c265567c881d07e", + "reference": "fc0fe8f4d0b527254a2dc45f0c265567c881d07e", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "org\\bovigo\\vfs": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD" + ], + "homepage": "http://vfs.bovigo.org/", + "time": "2012-08-25 12:49:29" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.2.4" + }, + "platform-dev": [] +} diff --git a/codeigniter-3.0/contributing.md b/ci-3.0/contributing.md similarity index 100% rename from codeigniter-3.0/contributing.md rename to ci-3.0/contributing.md diff --git a/ci-3.0/index.php b/ci-3.0/index.php new file mode 100755 index 000000000..86269334a --- /dev/null +++ b/ci-3.0/index.php @@ -0,0 +1,294 @@ +=')) + { + error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED); + } + else + { + error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_USER_NOTICE); + } + break; + + default: + header('HTTP/1.1 503 Service Unavailable.', TRUE, 503); + echo 'The application environment is not set correctly.'; + exit(1); // EXIT_ERROR +} + +/* + *--------------------------------------------------------------- + * SYSTEM FOLDER NAME + *--------------------------------------------------------------- + * + * This variable must contain the name of your "system" folder. + * Include the path if the folder is not in the same directory + * as this file. + */ + $system_path = 'system'; + +/* + *--------------------------------------------------------------- + * APPLICATION FOLDER NAME + *--------------------------------------------------------------- + * + * If you want this front controller to use a different "application" + * folder than the default one you can set its name here. The folder + * can also be renamed or relocated anywhere on your server. If + * you do, use a full server path. For more info please see the user guide: + * http://codeigniter.com/user_guide/general/managing_apps.html + * + * NO TRAILING SLASH! + */ + $application_folder = 'application'; + +/* + *--------------------------------------------------------------- + * VIEW FOLDER NAME + *--------------------------------------------------------------- + * + * If you want to move the view folder out of the application + * folder set the path to the folder here. The folder can be renamed + * and relocated anywhere on your server. If blank, it will default + * to the standard location inside your application folder. If you + * do move this, use the full server path to this folder. + * + * NO TRAILING SLASH! + */ + $view_folder = ''; + + +/* + * -------------------------------------------------------------------- + * DEFAULT CONTROLLER + * -------------------------------------------------------------------- + * + * Normally you will set your default controller in the routes.php file. + * You can, however, force a custom routing by hard-coding a + * specific controller class/function here. For most applications, you + * WILL NOT set your routing here, but it's an option for those + * special instances where you might want to override the standard + * routing in a specific front controller that shares a common CI installation. + * + * IMPORTANT: If you set the routing here, NO OTHER controller will be + * callable. In essence, this preference limits your application to ONE + * specific controller. Leave the function name blank if you need + * to call functions dynamically via the URI. + * + * Un-comment the $routing array below to use this feature + */ + // The directory name, relative to the "controllers" folder. Leave blank + // if your controller is not in a sub-folder within the "controllers" folder + // $routing['directory'] = ''; + + // The controller class file name. Example: mycontroller + // $routing['controller'] = ''; + + // The controller function you wish to be called. + // $routing['function'] = ''; + + +/* + * ------------------------------------------------------------------- + * CUSTOM CONFIG VALUES + * ------------------------------------------------------------------- + * + * The $assign_to_config array below will be passed dynamically to the + * config class when initialized. This allows you to set custom config + * items or override any default config values found in the config.php file. + * This can be handy as it permits you to share one application between + * multiple front controller files, with each file containing different + * config values. + * + * Un-comment the $assign_to_config array below to use this feature + */ + // $assign_to_config['name_of_config_item'] = 'value of config item'; + + + +// -------------------------------------------------------------------- +// END OF USER CONFIGURABLE SETTINGS. DO NOT EDIT BELOW THIS LINE +// -------------------------------------------------------------------- + +/* + * --------------------------------------------------------------- + * Resolve the system path for increased reliability + * --------------------------------------------------------------- + */ + + // Set the current directory correctly for CLI requests + if (defined('STDIN')) + { + chdir(dirname(__FILE__)); + } + + if (($_temp = realpath($system_path)) !== FALSE) + { + $system_path = $_temp.'/'; + } + else + { + // Ensure there's a trailing slash + $system_path = rtrim($system_path, '/').'/'; + } + + // Is the system path correct? + if ( ! is_dir($system_path)) + { + header('HTTP/1.1 503 Service Unavailable.', TRUE, 503); + echo 'Your system folder path does not appear to be set correctly. Please open the following file and correct this: '.pathinfo(__FILE__, PATHINFO_BASENAME); + exit(3); // EXIT_CONFIG + } + +/* + * ------------------------------------------------------------------- + * Now that we know the path, set the main path constants + * ------------------------------------------------------------------- + */ + // The name of THIS file + define('SELF', pathinfo(__FILE__, PATHINFO_BASENAME)); + + // Path to the system folder + define('BASEPATH', str_replace('\\', '/', $system_path)); + + // Path to the front controller (this file) + define('FCPATH', dirname(__FILE__).'/'); + + // Name of the "system folder" + define('SYSDIR', trim(strrchr(trim(BASEPATH, '/'), '/'), '/')); + + // The path to the "application" folder + if (is_dir($application_folder)) + { + if (($_temp = realpath($application_folder)) !== FALSE) + { + $application_folder = $_temp; + } + + define('APPPATH', $application_folder.DIRECTORY_SEPARATOR); + } + else + { + if ( ! is_dir(BASEPATH.$application_folder.DIRECTORY_SEPARATOR)) + { + header('HTTP/1.1 503 Service Unavailable.', TRUE, 503); + echo 'Your application folder path does not appear to be set correctly. Please open the following file and correct this: '.SELF; + exit(3); // EXIT_CONFIG + } + + define('APPPATH', BASEPATH.$application_folder.DIRECTORY_SEPARATOR); + } + + // The path to the "views" folder + if ( ! is_dir($view_folder)) + { + if ( ! empty($view_folder) && is_dir(APPPATH.$view_folder.DIRECTORY_SEPARATOR)) + { + $view_folder = APPPATH.$view_folder; + } + elseif ( ! is_dir(APPPATH.'views'.DIRECTORY_SEPARATOR)) + { + header('HTTP/1.1 503 Service Unavailable.', TRUE, 503); + echo 'Your view folder path does not appear to be set correctly. Please open the following file and correct this: '.SELF; + exit(3); // EXIT_CONFIG + } + else + { + $view_folder = APPPATH.'views'; + } + } + + if (($_temp = realpath($view_folder)) !== FALSE) + { + $view_folder = $_temp.DIRECTORY_SEPARATOR; + } + else + { + $view_folder = rtrim($view_folder, '/\\').DIRECTORY_SEPARATOR; + } + + define('VIEWPATH', $view_folder); + +/* + * -------------------------------------------------------------------- + * LOAD THE BOOTSTRAP FILE + * -------------------------------------------------------------------- + * + * And away we go... + */ +require_once BASEPATH.'core/CodeIgniter.php'; + +require $_SERVER['DOCUMENT_ROOT'].'/php-framework-benchmark/libs/output_data.php'; diff --git a/codeigniter-3.0/license.txt b/ci-3.0/license.txt similarity index 100% rename from codeigniter-3.0/license.txt rename to ci-3.0/license.txt diff --git a/codeigniter-3.0/readme.rst b/ci-3.0/readme.rst similarity index 100% rename from codeigniter-3.0/readme.rst rename to ci-3.0/readme.rst diff --git a/codeigniter-3.0/system/core/Benchmark.php b/ci-3.0/system/core/Benchmark.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/core/Benchmark.php rename to ci-3.0/system/core/Benchmark.php diff --git a/ci-3.0/system/core/CodeIgniter.php b/ci-3.0/system/core/CodeIgniter.php new file mode 100755 index 000000000..5080dc6d1 --- /dev/null +++ b/ci-3.0/system/core/CodeIgniter.php @@ -0,0 +1,541 @@ + '_ENV', 'G' => '_GET', 'P' => '_POST', 'C' => '_COOKIE', 'S' => '_SERVER') as $key => $superglobal) + { + if (strpos($_registered, $key) === FALSE) + { + continue; + } + + foreach (array_keys($$superglobal) as $var) + { + if (isset($GLOBALS[$var]) && ! in_array($var, $_protected, TRUE)) + { + $GLOBALS[$var] = NULL; + } + } + } + } +} + + +/* + * ------------------------------------------------------ + * Define a custom error handler so we can log PHP errors + * ------------------------------------------------------ + */ + set_error_handler('_error_handler'); + set_exception_handler('_exception_handler'); + register_shutdown_function('_shutdown_handler'); + +/* + * ------------------------------------------------------ + * Set the subclass_prefix + * ------------------------------------------------------ + * + * Normally the "subclass_prefix" is set in the config file. + * The subclass prefix allows CI to know if a core class is + * being extended via a library in the local application + * "libraries" folder. Since CI allows config items to be + * overridden via data set in the main index.php file, + * before proceeding we need to know if a subclass_prefix + * override exists. If so, we will set this value now, + * before any classes are loaded + * Note: Since the config file data is cached it doesn't + * hurt to load it here. + */ + if ( ! empty($assign_to_config['subclass_prefix'])) + { + get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix'])); + } + +/* + * ------------------------------------------------------ + * Should we use a Composer autoloader? + * ------------------------------------------------------ + */ + if ($composer_autoload = config_item('composer_autoload')) + { + if ($composer_autoload === TRUE) + { + file_exists(APPPATH.'vendor/autoload.php') + ? require_once(APPPATH.'vendor/autoload.php') + : log_message('error', '$config[\'composer_autoload\'] is set to TRUE but '.APPPATH.'vendor/autoload.php was not found.'); + } + elseif (file_exists($composer_autoload)) + { + require_once($composer_autoload); + } + else + { + log_message('error', 'Could not find the specified $config[\'composer_autoload\'] path: '.$composer_autoload); + } + } + +/* + * ------------------------------------------------------ + * Start the timer... tick tock tick tock... + * ------------------------------------------------------ + */ + $BM =& load_class('Benchmark', 'core'); + $BM->mark('total_execution_time_start'); + $BM->mark('loading_time:_base_classes_start'); + +/* + * ------------------------------------------------------ + * Instantiate the hooks class + * ------------------------------------------------------ + */ + $EXT =& load_class('Hooks', 'core'); + +/* + * ------------------------------------------------------ + * Is there a "pre_system" hook? + * ------------------------------------------------------ + */ + $EXT->call_hook('pre_system'); + +/* + * ------------------------------------------------------ + * Instantiate the config class + * ------------------------------------------------------ + * + * Note: It is important that Config is loaded first as + * most other classes depend on it either directly or by + * depending on another class that uses it. + * + */ + $CFG =& load_class('Config', 'core'); + + // Do we have any manually set config items in the index.php file? + if (isset($assign_to_config) && is_array($assign_to_config)) + { + foreach ($assign_to_config as $key => $value) + { + $CFG->set_item($key, $value); + } + } + +/* + * ------------------------------------------------------ + * Important charset-related stuff + * ------------------------------------------------------ + * + * Configure mbstring and/or iconv if they are enabled + * and set MB_ENABLED and ICONV_ENABLED constants, so + * that we don't repeatedly do extension_loaded() or + * function_exists() calls. + * + * Note: UTF-8 class depends on this. It used to be done + * in it's constructor, but it's _not_ class-specific. + * + */ + $charset = strtoupper(config_item('charset')); + ini_set('default_charset', $charset); + + if (extension_loaded('mbstring')) + { + define('MB_ENABLED', TRUE); + // mbstring.internal_encoding is deprecated starting with PHP 5.6 + // and it's usage triggers E_DEPRECATED messages. + @ini_set('mbstring.internal_encoding', $charset); + // This is required for mb_convert_encoding() to strip invalid characters. + // That's utilized by CI_Utf8, but it's also done for consistency with iconv. + mb_substitute_character('none'); + } + else + { + define('MB_ENABLED', FALSE); + } + + // There's an ICONV_IMPL constant, but the PHP manual says that using + // iconv's predefined constants is "strongly discouraged". + if (extension_loaded('iconv')) + { + define('ICONV_ENABLED', TRUE); + // iconv.internal_encoding is deprecated starting with PHP 5.6 + // and it's usage triggers E_DEPRECATED messages. + @ini_set('iconv.internal_encoding', $charset); + } + else + { + define('ICONV_ENABLED', FALSE); + } + + if (is_php('5.6')) + { + ini_set('php.internal_encoding', $charset); + } + +/* + * ------------------------------------------------------ + * Load compatibility features + * ------------------------------------------------------ + */ + + require_once(BASEPATH.'core/compat/mbstring.php'); + require_once(BASEPATH.'core/compat/hash.php'); + require_once(BASEPATH.'core/compat/password.php'); + require_once(BASEPATH.'core/compat/standard.php'); + +/* + * ------------------------------------------------------ + * Instantiate the UTF-8 class + * ------------------------------------------------------ + */ + $UNI =& load_class('Utf8', 'core'); + +/* + * ------------------------------------------------------ + * Instantiate the URI class + * ------------------------------------------------------ + */ + $URI =& load_class('URI', 'core'); + +/* + * ------------------------------------------------------ + * Instantiate the routing class and set the routing + * ------------------------------------------------------ + */ + $RTR =& load_class('Router', 'core', isset($routing) ? $routing : NULL); + +/* + * ------------------------------------------------------ + * Instantiate the output class + * ------------------------------------------------------ + */ + $OUT =& load_class('Output', 'core'); + +/* + * ------------------------------------------------------ + * Is there a valid cache file? If so, we're done... + * ------------------------------------------------------ + */ + if ($EXT->call_hook('cache_override') === FALSE && $OUT->_display_cache($CFG, $URI) === TRUE) + { + exit; + } + +/* + * ----------------------------------------------------- + * Load the security class for xss and csrf support + * ----------------------------------------------------- + */ + $SEC =& load_class('Security', 'core'); + +/* + * ------------------------------------------------------ + * Load the Input class and sanitize globals + * ------------------------------------------------------ + */ + $IN =& load_class('Input', 'core'); + +/* + * ------------------------------------------------------ + * Load the Language class + * ------------------------------------------------------ + */ + $LANG =& load_class('Lang', 'core'); + +/* + * ------------------------------------------------------ + * Load the app controller and local controller + * ------------------------------------------------------ + * + */ + // Load the base controller class + require_once BASEPATH.'core/Controller.php'; + + /** + * Reference to the CI_Controller method. + * + * Returns current CI instance object + * + * @return object + */ + function &get_instance() + { + return CI_Controller::get_instance(); + } + + if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php')) + { + require_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'; + } + + // Set a mark point for benchmarking + $BM->mark('loading_time:_base_classes_end'); + +/* + * ------------------------------------------------------ + * Sanity checks + * ------------------------------------------------------ + * + * The Router class has already validated the request, + * leaving us with 3 options here: + * + * 1) an empty class name, if we reached the default + * controller, but it didn't exist; + * 2) a query string which doesn't go through a + * file_exists() check + * 3) a regular request for a non-existing page + * + * We handle all of these as a 404 error. + * + * Furthermore, none of the methods in the app controller + * or the loader class can be called via the URI, nor can + * controller methods that begin with an underscore. + */ + + $e404 = FALSE; + $class = ucfirst($RTR->class); + $method = $RTR->method; + + if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php')) + { + $e404 = TRUE; + } + else + { + require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php'); + + if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method)) + { + $e404 = TRUE; + } + elseif (method_exists($class, '_remap')) + { + $params = array($method, array_slice($URI->rsegments, 2)); + $method = '_remap'; + } + // WARNING: It appears that there are issues with is_callable() even in PHP 5.2! + // Furthermore, there are bug reports and feature/change requests related to it + // that make it unreliable to use in this context. Please, DO NOT change this + // work-around until a better alternative is available. + elseif ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($class)), TRUE)) + { + $e404 = TRUE; + } + } + + if ($e404) + { + if ( ! empty($RTR->routes['404_override'])) + { + if (sscanf($RTR->routes['404_override'], '%[^/]/%s', $error_class, $error_method) !== 2) + { + $error_method = 'index'; + } + + $error_class = ucfirst($error_class); + + if ( ! class_exists($error_class, FALSE)) + { + if (file_exists(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php')) + { + require_once(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php'); + $e404 = ! class_exists($error_class, FALSE); + } + // Were we in a directory? If so, check for a global override + elseif ( ! empty($RTR->directory) && file_exists(APPPATH.'controllers/'.$error_class.'.php')) + { + require_once(APPPATH.'controllers/'.$error_class.'.php'); + if (($e404 = ! class_exists($error_class, FALSE)) === FALSE) + { + $RTR->directory = ''; + } + } + } + else + { + $e404 = FALSE; + } + } + + // Did we reset the $e404 flag? If so, set the rsegments, starting from index 1 + if ( ! $e404) + { + $class = $error_class; + $method = $error_method; + + $URI->rsegments = array( + 1 => $class, + 2 => $method + ); + } + else + { + show_404($RTR->directory.$class.'/'.$method); + } + } + + if ($method !== '_remap') + { + $params = array_slice($URI->rsegments, 2); + } + +/* + * ------------------------------------------------------ + * Is there a "pre_controller" hook? + * ------------------------------------------------------ + */ + $EXT->call_hook('pre_controller'); + +/* + * ------------------------------------------------------ + * Instantiate the requested controller + * ------------------------------------------------------ + */ + // Mark a start point so we can benchmark the controller + $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start'); + + $CI = new $class(); + +/* + * ------------------------------------------------------ + * Is there a "post_controller_constructor" hook? + * ------------------------------------------------------ + */ + $EXT->call_hook('post_controller_constructor'); + +/* + * ------------------------------------------------------ + * Call the requested method + * ------------------------------------------------------ + */ + call_user_func_array(array(&$CI, $method), $params); + + // Mark a benchmark end point + $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end'); + +/* + * ------------------------------------------------------ + * Is there a "post_controller" hook? + * ------------------------------------------------------ + */ + $EXT->call_hook('post_controller'); + +/* + * ------------------------------------------------------ + * Send the final rendered output to the browser + * ------------------------------------------------------ + */ + if ($EXT->call_hook('display_override') === FALSE) + { + $OUT->_display(); + } + +/* + * ------------------------------------------------------ + * Is there a "post_system" hook? + * ------------------------------------------------------ + */ + $EXT->call_hook('post_system'); diff --git a/ci-3.0/system/core/Common.php b/ci-3.0/system/core/Common.php new file mode 100755 index 000000000..3ab98cf6d --- /dev/null +++ b/ci-3.0/system/core/Common.php @@ -0,0 +1,851 @@ +='); + } + + return $_is_php[$version]; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('is_really_writable')) +{ + /** + * Tests for file writability + * + * is_writable() returns TRUE on Windows servers when you really can't write to + * 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 + */ + function is_really_writable($file) + { + // If we're on a Unix server with safe_mode off we call is_writable + if (DIRECTORY_SEPARATOR === '/' && (is_php('5.4') OR ! ini_get('safe_mode'))) + { + return is_writable($file); + } + + /* For Windows servers and safe_mode "on" installations we'll actually + * write a file then read it. Bah... + */ + if (is_dir($file)) + { + $file = rtrim($file, '/').'/'.md5(mt_rand()); + if (($fp = @fopen($file, 'ab')) === FALSE) + { + return FALSE; + } + + fclose($fp); + @chmod($file, 0777); + @unlink($file); + return TRUE; + } + elseif ( ! is_file($file) OR ($fp = @fopen($file, 'ab')) === FALSE) + { + return FALSE; + } + + fclose($fp); + return TRUE; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('load_class')) +{ + /** + * Class registry + * + * This function acts as a singleton. If the requested class does not + * exist it is instantiated and set to a static variable. If it has + * previously been instantiated the variable is returned. + * + * @param string the class name being requested + * @param string the directory where the class should be found + * @param string an optional argument to pass to the class constructor + * @return object + */ + function &load_class($class, $directory = 'libraries', $param = NULL) + { + static $_classes = array(); + + // Does the class exist? If so, we're done... + if (isset($_classes[$class])) + { + return $_classes[$class]; + } + + $name = FALSE; + + // Look for the class first in the local application/libraries folder + // then in the native system/libraries folder + foreach (array(APPPATH, BASEPATH) as $path) + { + if (file_exists($path.$directory.'/'.$class.'.php')) + { + $name = 'CI_'.$class; + + if (class_exists($name, FALSE) === FALSE) + { + require_once($path.$directory.'/'.$class.'.php'); + } + + break; + } + } + + // Is the request a class extension? If so we load it too + if (file_exists(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php')) + { + $name = config_item('subclass_prefix').$class; + + if (class_exists($name, FALSE) === FALSE) + { + require_once(APPPATH.$directory.'/'.$name.'.php'); + } + } + + // Did we find the class? + if ($name === FALSE) + { + // Note: We use exit() rather than show_error() in order to avoid a + // self-referencing loop with the Exceptions class + set_status_header(503); + echo 'Unable to locate the specified class: '.$class.'.php'; + exit(5); // EXIT_UNK_CLASS + } + + // Keep track of what we just loaded + is_loaded($class); + + $_classes[$class] = isset($param) + ? new $name($param) + : new $name(); + return $_classes[$class]; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('is_loaded')) +{ + /** + * Keeps track of which libraries have been loaded. This function is + * called by the load_class() function above + * + * @param string + * @return array + */ + function &is_loaded($class = '') + { + static $_is_loaded = array(); + + if ($class !== '') + { + $_is_loaded[strtolower($class)] = $class; + } + + return $_is_loaded; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('get_config')) +{ + /** + * Loads the main config.php file + * + * This function lets us grab the config file even if the Config class + * hasn't been instantiated yet + * + * @param array + * @return array + */ + function &get_config(Array $replace = array()) + { + static $config; + + if (empty($config)) + { + $file_path = APPPATH.'config/config.php'; + $found = FALSE; + if (file_exists($file_path)) + { + $found = TRUE; + require($file_path); + } + + // Is the config file in the environment folder? + if (file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php')) + { + require($file_path); + } + elseif ( ! $found) + { + set_status_header(503); + echo 'The configuration file does not exist.'; + exit(3); // EXIT_CONFIG + } + + // Does the $config array exist in the file? + if ( ! isset($config) OR ! is_array($config)) + { + set_status_header(503); + echo 'Your config file does not appear to be formatted correctly.'; + exit(3); // EXIT_CONFIG + } + } + + // Are any values being dynamically added or replaced? + foreach ($replace as $key => $val) + { + $config[$key] = $val; + } + + return $config; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('config_item')) +{ + /** + * Returns the specified config item + * + * @param string + * @return mixed + */ + function config_item($item) + { + static $_config; + + if (empty($_config)) + { + // references cannot be directly assigned to static variables, so we use an array + $_config[0] =& get_config(); + } + + return isset($_config[0][$item]) ? $_config[0][$item] : NULL; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('get_mimes')) +{ + /** + * Returns the MIME types array from config/mimes.php + * + * @return array + */ + function &get_mimes() + { + static $_mimes; + + if (empty($_mimes)) + { + if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php')) + { + $_mimes = include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'); + } + elseif (file_exists(APPPATH.'config/mimes.php')) + { + $_mimes = include(APPPATH.'config/mimes.php'); + } + else + { + $_mimes = array(); + } + } + + return $_mimes; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('is_https')) +{ + /** + * Is HTTPS? + * + * Determines if the application is accessed via an encrypted + * (HTTPS) connection. + * + * @return bool + */ + function is_https() + { + if ( ! empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') + { + return TRUE; + } + elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') + { + return TRUE; + } + elseif ( ! empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off') + { + return TRUE; + } + + return FALSE; + } +} + +// ------------------------------------------------------------------------ + +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' OR defined('STDIN')); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('show_error')) +{ + /** + * Error Handler + * + * This function lets us invoke the exception class and + * display errors using the standard error template located + * in application/views/errors/error_general.php + * This function will send the error page directly to the + * browser and exit. + * + * @param string + * @param int + * @param string + * @return void + */ + function show_error($message, $status_code = 500, $heading = 'An Error Was Encountered') + { + $status_code = abs($status_code); + if ($status_code < 100) + { + $exit_status = $status_code + 9; // 9 is EXIT__AUTO_MIN + if ($exit_status > 125) // 125 is EXIT__AUTO_MAX + { + $exit_status = 1; // EXIT_ERROR + } + + $status_code = 500; + } + else + { + $exit_status = 1; // EXIT_ERROR + } + + $_error =& load_class('Exceptions', 'core'); + echo $_error->show_error($heading, $message, 'error_general', $status_code); + exit($exit_status); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('show_404')) +{ + /** + * 404 Page Handler + * + * This function is similar to the show_error() function above + * However, instead of the standard error template it displays + * 404 errors. + * + * @param string + * @param bool + * @return void + */ + function show_404($page = '', $log_error = TRUE) + { + $_error =& load_class('Exceptions', 'core'); + $_error->show_404($page, $log_error); + exit(4); // EXIT_UNKNOWN_FILE + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('log_message')) +{ + /** + * Error Logging Interface + * + * We use this as a simple mechanism to access the logging + * class and send messages to be logged. + * + * @param string the error level: 'error', 'debug' or 'info' + * @param string the error message + * @return void + */ + function log_message($level, $message) + { + static $_log; + + if ($_log === NULL) + { + // references cannot be directly assigned to static variables, so we use an array + $_log[0] =& load_class('Log', 'core'); + } + + $_log[0]->write_log($level, $message); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('set_status_header')) +{ + /** + * Set HTTP Status Header + * + * @param int the status code + * @param string + * @return void + */ + function set_status_header($code = 200, $text = '') + { + if (is_cli()) + { + return; + } + + if (empty($code) OR ! is_numeric($code)) + { + show_error('Status codes must be numeric', 500); + } + + if (empty($text)) + { + is_int($code) OR $code = (int) $code; + $stati = array( + 100 => 'Continue', + 101 => 'Switching Protocols', + + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 422 => 'Unprocessable Entity', + + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported' + ); + + if (isset($stati[$code])) + { + $text = $stati[$code]; + } + else + { + show_error('No status text available. Please check your status code number or supply your own message text.', 500); + } + } + + if (strpos(PHP_SAPI, 'cgi') === 0) + { + header('Status: '.$code.' '.$text, TRUE); + } + else + { + $server_protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'; + header($server_protocol.' '.$code.' '.$text, TRUE, $code); + } + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('_error_handler')) +{ + /** + * Error Handler + * + * This is the custom error handler that is declared at the (relative) + * top of CodeIgniter.php. The main reason we use this is to permit + * PHP errors to be logged in our own log files since the user may + * not have access to server logs. Since this function effectively + * intercepts PHP errors, however, we also need to display errors + * based on the current error_reporting level. + * We do that with the use of a PHP error template. + * + * @param int $severity + * @param string $message + * @param string $filepath + * @param int $line + * @return void + */ + function _error_handler($severity, $message, $filepath, $line) + { + $is_error = (((E_ERROR | E_COMPILE_ERROR | E_CORE_ERROR | E_USER_ERROR) & $severity) === $severity); + + // When an error occurred, set the status header to '500 Internal Server Error' + // to indicate to the client something went wrong. + // This can't be done within the $_error->show_php_error method because + // it is only called when the display_errors flag is set (which isn't usually + // the case in a production environment) or when errors are ignored because + // they are above the error_reporting threshold. + if ($is_error) + { + set_status_header(500); + } + + // Should we ignore the error? We'll get the current error_reporting + // level and add its bits with the severity bits to find out. + if (($severity & error_reporting()) !== $severity) + { + return; + } + + $_error =& load_class('Exceptions', 'core'); + $_error->log_exception($severity, $message, $filepath, $line); + + // Should we display the error? + if (str_ireplace(array('off', 'none', 'no', 'false', 'null'), '', ini_get('display_errors'))) + { + $_error->show_php_error($severity, $message, $filepath, $line); + } + + // If the error is fatal, the execution of the script should be stopped because + // errors can't be recovered from. Halting the script conforms with PHP's + // default error handling. See http://www.php.net/manual/en/errorfunc.constants.php + if ($is_error) + { + exit(1); // EXIT_ERROR + } + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('_exception_handler')) +{ + /** + * Exception Handler + * + * Sends uncaught exceptions to the logger and displays them + * only if display_errors is On so that they don't show up in + * production environments. + * + * @param Exception $exception + * @return void + */ + function _exception_handler($exception) + { + $_error =& load_class('Exceptions', 'core'); + $_error->log_exception('error', 'Exception: '.$exception->getMessage(), $exception->getFile(), $exception->getLine()); + + // Should we display the error? + if (str_ireplace(array('off', 'none', 'no', 'false', 'null'), '', ini_get('display_errors'))) + { + $_error->show_exception($exception); + } + + exit(1); // EXIT_ERROR + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('_shutdown_handler')) +{ + /** + * Shutdown Handler + * + * This is the shutdown handler that is declared at the top + * of CodeIgniter.php. The main reason we use this is to simulate + * a complete custom exception handler. + * + * E_STRICT is purposively neglected because such events may have + * been caught. Duplication or none? None is preferred for now. + * + * @link http://insomanic.me.uk/post/229851073/php-trick-catching-fatal-errors-e-error-with-a + * @return void + */ + function _shutdown_handler() + { + $last_error = error_get_last(); + if (isset($last_error) && + ($last_error['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING))) + { + _error_handler($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']); + } + } +} + +// -------------------------------------------------------------------- + +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; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('html_escape')) +{ + /** + * Returns HTML escaped variable. + * + * @param mixed $var The input string or array of strings to be escaped. + * @param bool $double_encode $double_encode set to FALSE prevents escaping twice. + * @return mixed The escaped string or array of strings as a result. + */ + function html_escape($var, $double_encode = TRUE) + { + if (empty($var)) + { + return $var; + } + + if (is_array($var)) + { + foreach (array_keys($var) as $key) + { + $var[$key] = html_escape($var[$key], $double_encode); + } + + return $var; + } + + return htmlspecialchars($var, ENT_QUOTES, config_item('charset'), $double_encode); + } +} + +// ------------------------------------------------------------------------ + +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) + { + $atts = NULL; + + if (empty($attributes)) + { + return $atts; + } + + if (is_string($attributes)) + { + return ' '.$attributes; + } + + $attributes = (array) $attributes; + + foreach ($attributes as $key => $val) + { + $atts .= ($js) ? $key.'='.$val.',' : ' '.$key.'="'.$val.'"'; + } + + return rtrim($atts, ','); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('function_usable')) +{ + /** + * Function usable + * + * Executes a function_exists() check, and if the Suhosin PHP + * extension is loaded - checks whether the function that is + * checked might be disabled in there as well. + * + * This is useful as function_exists() will return FALSE for + * functions disabled via the *disable_functions* php.ini + * setting, but not for *suhosin.executor.func.blacklist* and + * *suhosin.executor.disable_eval*. These settings will just + * terminate script execution if a disabled function is executed. + * + * The above described behavior turned out to be a bug in Suhosin, + * but even though a fix was commited for 0.9.34 on 2012-02-12, + * that version is yet to be released. This function will therefore + * be just temporary, but would probably be kept for a few years. + * + * @link http://www.hardened-php.net/suhosin/ + * @param string $function_name Function to check for + * @return bool TRUE if the function exists and is safe to call, + * FALSE otherwise. + */ + function function_usable($function_name) + { + static $_suhosin_func_blacklist; + + if (function_exists($function_name)) + { + if ( ! isset($_suhosin_func_blacklist)) + { + $_suhosin_func_blacklist = extension_loaded('suhosin') + ? explode(',', trim(ini_get('suhosin.executor.func.blacklist'))) + : array(); + } + + return ! in_array($function_name, $_suhosin_func_blacklist, TRUE); + } + + return FALSE; + } +} diff --git a/ci-3.0/system/core/Config.php b/ci-3.0/system/core/Config.php new file mode 100755 index 000000000..0264776f9 --- /dev/null +++ b/ci-3.0/system/core/Config.php @@ -0,0 +1,373 @@ +config =& get_config(); + + // Set the base_url automatically if none was provided + if (empty($this->config['base_url'])) + { + if (isset($_SERVER['SERVER_ADDR'])) + { + $base_url = (is_https() ? 'https' : 'http').'://'.$_SERVER['SERVER_ADDR'] + .substr($_SERVER['SCRIPT_NAME'], 0, strpos($_SERVER['SCRIPT_NAME'], basename($_SERVER['SCRIPT_FILENAME']))); + } + else + { + $base_url = 'http://localhost/'; + } + + $this->set_item('base_url', $base_url); + } + + log_message('info', 'Config Class Initialized'); + } + + // -------------------------------------------------------------------- + + /** + * Load Config File + * + * @param string $file Configuration file name + * @param bool $use_sections Whether configuration values should be loaded into their own section + * @param bool $fail_gracefully Whether to just return FALSE or display an error message + * @return bool TRUE if the file was loaded correctly or FALSE on failure + */ + public function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE) + { + $file = ($file === '') ? 'config' : str_replace('.php', '', $file); + $loaded = FALSE; + + foreach ($this->_config_paths as $path) + { + foreach (array($file, ENVIRONMENT.DIRECTORY_SEPARATOR.$file) as $location) + { + $file_path = $path.'config/'.$location.'.php'; + if (in_array($file_path, $this->is_loaded, TRUE)) + { + return TRUE; + } + + if ( ! file_exists($file_path)) + { + continue; + } + + include($file_path); + + if ( ! isset($config) OR ! is_array($config)) + { + if ($fail_gracefully === TRUE) + { + return FALSE; + } + + show_error('Your '.$file_path.' file does not appear to contain a valid configuration array.'); + } + + if ($use_sections === TRUE) + { + $this->config[$file] = isset($this->config[$file]) + ? array_merge($this->config[$file], $config) + : $config; + } + else + { + $this->config = array_merge($this->config, $config); + } + + $this->is_loaded[] = $file_path; + $config = NULL; + $loaded = TRUE; + log_message('debug', 'Config file loaded: '.$file_path); + } + } + + if ($loaded === TRUE) + { + return TRUE; + } + elseif ($fail_gracefully === TRUE) + { + return FALSE; + } + + show_error('The configuration file '.$file.'.php does not exist.'); + } + + // -------------------------------------------------------------------- + + /** + * Fetch a config file item + * + * @param string $item Config item name + * @param string $index Index name + * @return string|null The configuration item or NULL if the item doesn't exist + */ + public function item($item, $index = '') + { + if ($index == '') + { + return isset($this->config[$item]) ? $this->config[$item] : NULL; + } + + return isset($this->config[$index], $this->config[$index][$item]) ? $this->config[$index][$item] : NULL; + } + + // -------------------------------------------------------------------- + + /** + * 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 + */ + public function slash_item($item) + { + if ( ! isset($this->config[$item])) + { + return NULL; + } + elseif (trim($this->config[$item]) === '') + { + return ''; + } + + return rtrim($this->config[$item], '/').'/'; + } + + // -------------------------------------------------------------------- + + /** + * Site URL + * + * Returns base_url . index_page [. uri_string] + * + * @uses CI_Config::_uri_string() + * + * @param string|string[] $uri URI string or an array of segments + * @param string $protocol + * @return string + */ + public function site_url($uri = '', $protocol = NULL) + { + $base_url = $this->slash_item('base_url'); + + if (isset($protocol)) + { + // For protocol-relative links + if ($protocol === '') + { + $base_url = substr($base_url, strpos($base_url, '//')); + } + else + { + $base_url = $protocol.substr($base_url, strpos($base_url, '://')); + } + } + + if (empty($uri)) + { + return $base_url.$this->item('index_page'); + } + + $uri = $this->_uri_string($uri); + + if ($this->item('enable_query_strings') === FALSE) + { + $suffix = isset($this->config['url_suffix']) ? $this->config['url_suffix'] : ''; + + if ($suffix !== '') + { + if (($offset = strpos($uri, '?')) !== FALSE) + { + $uri = substr($uri, 0, $offset).$suffix.substr($uri, $offset); + } + else + { + $uri .= $suffix; + } + } + + return $base_url.$this->slash_item('index_page').$uri; + } + elseif (strpos($uri, '?') === FALSE) + { + $uri = '?'.$uri; + } + + return $base_url.$this->item('index_page').$uri; + } + + // ------------------------------------------------------------- + + /** + * Base URL + * + * Returns base_url [. uri_string] + * + * @uses CI_Config::_uri_string() + * + * @param string|string[] $uri URI string or an array of segments + * @param string $protocol + * @return string + */ + public function base_url($uri = '', $protocol = NULL) + { + $base_url = $this->slash_item('base_url'); + + if (isset($protocol)) + { + // For protocol-relative links + if ($protocol === '') + { + $base_url = substr($base_url, strpos($base_url, '//')); + } + else + { + $base_url = $protocol.substr($base_url, strpos($base_url, '://')); + } + } + + return $base_url.ltrim($this->_uri_string($uri), '/'); + } + + // ------------------------------------------------------------- + + /** + * Build URI string + * + * @used-by CI_Config::site_url() + * @used-by CI_Config::base_url() + * + * @param string|string[] $uri URI string or an array of segments + * @return string + */ + protected function _uri_string($uri) + { + if ($this->item('enable_query_strings') === FALSE) + { + if (is_array($uri)) + { + $uri = implode('/', $uri); + } + return trim($uri, '/'); + } + elseif (is_array($uri)) + { + return http_build_query($uri); + } + + return $uri; + } + + // -------------------------------------------------------------------- + + /** + * System URL + * + * @deprecated 3.0.0 Encourages insecure practices + * @return string + */ + public function system_url() + { + $x = explode('/', preg_replace('|/*(.+?)/*$|', '\\1', BASEPATH)); + return $this->slash_item('base_url').end($x).'/'; + } + + // -------------------------------------------------------------------- + + /** + * Set a config file item + * + * @param string $item Config item key + * @param string $value Config item value + * @return void + */ + public function set_item($item, $value) + { + $this->config[$item] = $value; + } + +} diff --git a/codeigniter-3.0/system/core/Controller.php b/ci-3.0/system/core/Controller.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/core/Controller.php rename to ci-3.0/system/core/Controller.php diff --git a/ci-3.0/system/core/Exceptions.php b/ci-3.0/system/core/Exceptions.php new file mode 100755 index 000000000..d8f62c0fe --- /dev/null +++ b/ci-3.0/system/core/Exceptions.php @@ -0,0 +1,275 @@ + 'Error', + E_WARNING => 'Warning', + E_PARSE => 'Parsing Error', + E_NOTICE => 'Notice', + E_CORE_ERROR => 'Core Error', + E_CORE_WARNING => 'Core Warning', + E_COMPILE_ERROR => 'Compile Error', + E_COMPILE_WARNING => 'Compile Warning', + E_USER_ERROR => 'User Error', + E_USER_WARNING => 'User Warning', + E_USER_NOTICE => 'User Notice', + E_STRICT => 'Runtime Notice' + ); + + /** + * Class constructor + * + * @return void + */ + public function __construct() + { + $this->ob_level = ob_get_level(); + // Note: Do not log messages from this constructor. + } + + // -------------------------------------------------------------------- + + /** + * Exception Logger + * + * Logs PHP generated error messages + * + * @param int $severity Log level + * @param string $message Error message + * @param string $filepath File path + * @param int $line Line number + * @return void + */ + public function log_exception($severity, $message, $filepath, $line) + { + $severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity; + log_message('error', 'Severity: '.$severity.' --> '.$message.' '.$filepath.' '.$line); + } + + // -------------------------------------------------------------------- + + /** + * 404 Error Handler + * + * @uses CI_Exceptions::show_error() + * + * @param string $page Page URI + * @param bool $log_error Whether to log the error + * @return void + */ + public function show_404($page = '', $log_error = TRUE) + { + if (is_cli()) + { + $heading = 'Not Found'; + $message = 'The controller/method pair you requested was not found.'; + } + else + { + $heading = '404 Page Not Found'; + $message = 'The page you requested was not found.'; + } + + // By default we log this, but allow a dev to skip it + if ($log_error) + { + log_message('error', $heading.': '.$page); + } + + echo $this->show_error($heading, $message, 'error_404', 404); + exit(4); // EXIT_UNKNOWN_FILE + } + + // -------------------------------------------------------------------- + + /** + * General Error Page + * + * Takes an error message as input (either as a string or an array) + * and displays it using the specified template. + * + * @param string $heading Page heading + * @param string|string[] $message Error message + * @param string $template Template name + * @param int $status_code (default: 500) + * + * @return string Error page output + */ + public function show_error($heading, $message, $template = 'error_general', $status_code = 500) + { + $templates_path = config_item('error_views_path'); + if (empty($templates_path)) + { + $templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR; + } + + if (is_cli()) + { + $message = "\t".(is_array($message) ? implode("\n\t", $message) : $message); + $template = 'cli'.DIRECTORY_SEPARATOR.$template; + } + else + { + set_status_header($status_code); + $message = '

'.(is_array($message) ? implode('

', $message) : $message).'

'; + $template = 'html'.DIRECTORY_SEPARATOR.$template; + } + + if (ob_get_level() > $this->ob_level + 1) + { + ob_end_flush(); + } + ob_start(); + include($templates_path.$template.'.php'); + $buffer = ob_get_contents(); + ob_end_clean(); + return $buffer; + } + + // -------------------------------------------------------------------- + + public function show_exception($exception) + { + $templates_path = config_item('error_views_path'); + if (empty($templates_path)) + { + $templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR; + } + + $message = $exception->getMessage(); + if (empty($message)) + { + $message = '(null)'; + } + + if (is_cli()) + { + $templates_path .= 'cli'.DIRECTORY_SEPARATOR; + } + else + { + set_status_header(500); + $templates_path .= 'html'.DIRECTORY_SEPARATOR; + } + + if (ob_get_level() > $this->ob_level + 1) + { + ob_end_flush(); + } + + ob_start(); + include($templates_path.'error_exception.php'); + $buffer = ob_get_contents(); + ob_end_clean(); + echo $buffer; + } + + // -------------------------------------------------------------------- + + /** + * Native PHP error handler + * + * @param int $severity Error level + * @param string $message Error message + * @param string $filepath File path + * @param int $line Line number + * @return string Error page output + */ + public function show_php_error($severity, $message, $filepath, $line) + { + $templates_path = config_item('error_views_path'); + if (empty($templates_path)) + { + $templates_path = VIEWPATH.'errors'.DIRECTORY_SEPARATOR; + } + + $severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity; + + // For safety reasons we don't show the full file path in non-CLI requests + if ( ! is_cli()) + { + $filepath = str_replace('\\', '/', $filepath); + if (FALSE !== strpos($filepath, '/')) + { + $x = explode('/', $filepath); + $filepath = $x[count($x)-2].'/'.end($x); + } + + $template = 'html'.DIRECTORY_SEPARATOR.'error_php'; + } + else + { + $template = 'cli'.DIRECTORY_SEPARATOR.'error_php'; + } + + if (ob_get_level() > $this->ob_level + 1) + { + ob_end_flush(); + } + ob_start(); + include($templates_path.$template.'.php'); + $buffer = ob_get_contents(); + ob_end_clean(); + echo $buffer; + } + +} diff --git a/ci-3.0/system/core/Hooks.php b/ci-3.0/system/core/Hooks.php new file mode 100755 index 000000000..3b4fb2250 --- /dev/null +++ b/ci-3.0/system/core/Hooks.php @@ -0,0 +1,266 @@ +item('enable_hooks') === FALSE) + { + return; + } + + // Grab the "hooks" definition file. + if (file_exists(APPPATH.'config/hooks.php')) + { + include(APPPATH.'config/hooks.php'); + } + + if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/hooks.php')) + { + include(APPPATH.'config/'.ENVIRONMENT.'/hooks.php'); + } + + // If there are no hooks, we're done. + if ( ! isset($hook) OR ! is_array($hook)) + { + return; + } + + $this->hooks =& $hook; + $this->enabled = TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Call Hook + * + * Calls a particular hook. Called by CodeIgniter.php. + * + * @uses CI_Hooks::_run_hook() + * + * @param string $which Hook name + * @return bool TRUE on success or FALSE on failure + */ + public function call_hook($which = '') + { + if ( ! $this->enabled OR ! isset($this->hooks[$which])) + { + return FALSE; + } + + if (is_array($this->hooks[$which]) && ! isset($this->hooks[$which]['function'])) + { + foreach ($this->hooks[$which] as $val) + { + $this->_run_hook($val); + } + } + else + { + $this->_run_hook($this->hooks[$which]); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Run Hook + * + * Runs a particular hook + * + * @param array $data Hook details + * @return bool TRUE on success or FALSE on failure + */ + protected function _run_hook($data) + { + // Closures/lambda functions and array($object, 'method') callables + if (is_callable($data)) + { + is_array($data) + ? $data[0]->{$data[1]}() + : $data(); + + return TRUE; + } + elseif ( ! is_array($data)) + { + return FALSE; + } + + // ----------------------------------- + // Safety - Prevents run-away loops + // ----------------------------------- + + // If the script being called happens to have the same + // hook call within it a loop can happen + if ($this->_in_progress === TRUE) + { + return; + } + + // ----------------------------------- + // Set file path + // ----------------------------------- + + if ( ! isset($data['filepath'], $data['filename'])) + { + return FALSE; + } + + $filepath = APPPATH.$data['filepath'].'/'.$data['filename']; + + if ( ! file_exists($filepath)) + { + return FALSE; + } + + // Determine and class and/or function names + $class = empty($data['class']) ? FALSE : $data['class']; + $function = empty($data['function']) ? FALSE : $data['function']; + $params = isset($data['params']) ? $data['params'] : ''; + + if (empty($function)) + { + return FALSE; + } + + // Set the _in_progress flag + $this->_in_progress = TRUE; + + // Call the requested class and/or function + if ($class !== FALSE) + { + // The object is stored? + if (isset($this->_objects[$class])) + { + if (method_exists($this->_objects[$class], $function)) + { + $this->_objects[$class]->$function($params); + } + else + { + return $this->_in_progress = FALSE; + } + } + else + { + class_exists($class, FALSE) OR require_once($filepath); + + if ( ! class_exists($class, FALSE) OR ! method_exists($class, $function)) + { + return $this->_in_progress = FALSE; + } + + // Store the object and execute the method + $this->_objects[$class] = new $class(); + $this->_objects[$class]->$function($params); + } + } + else + { + function_exists($function) OR require_once($filepath); + + if ( ! function_exists($function)) + { + return $this->_in_progress = FALSE; + } + + $function($params); + } + + $this->_in_progress = FALSE; + return TRUE; + } + +} diff --git a/ci-3.0/system/core/Input.php b/ci-3.0/system/core/Input.php new file mode 100755 index 000000000..4e7a4e95e --- /dev/null +++ b/ci-3.0/system/core/Input.php @@ -0,0 +1,895 @@ +_allow_get_array = (config_item('allow_get_array') === TRUE); + $this->_enable_xss = (config_item('global_xss_filtering') === TRUE); + $this->_enable_csrf = (config_item('csrf_protection') === TRUE); + $this->_standardize_newlines = (bool) config_item('standardize_newlines'); + + $this->security =& load_class('Security', 'core'); + + // Do we need the UTF-8 class? + if (UTF8_ENABLED === TRUE) + { + $this->uni =& load_class('Utf8', 'core'); + } + + // Sanitize global arrays + $this->_sanitize_globals(); + + // CSRF Protection check + if ($this->_enable_csrf === TRUE && ! is_cli()) + { + $this->security->csrf_verify(); + } + + log_message('info', 'Input Class Initialized'); + } + + // -------------------------------------------------------------------- + + /** + * Fetch from array + * + * Internal method used to retrieve values from global arrays. + * + * @param array &$array $_GET, $_POST, $_COOKIE, $_SERVER, etc. + * @param mixed $index Index for item to be fetched from $array + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed + */ + protected function _fetch_from_array(&$array, $index = NULL, $xss_clean = NULL) + { + is_bool($xss_clean) OR $xss_clean = $this->_enable_xss; + + // If $index is NULL, it means that the whole $array is requested + isset($index) OR $index = array_keys($array); + + // allow fetching multiple keys at once + if (is_array($index)) + { + $output = array(); + foreach ($index as $key) + { + $output[$key] = $this->_fetch_from_array($array, $key, $xss_clean); + } + + return $output; + } + + if (isset($array[$index])) + { + $value = $array[$index]; + } + elseif (($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) // Does the index contain array notation + { + $value = $array; + for ($i = 0; $i < $count; $i++) + { + $key = trim($matches[0][$i], '[]'); + if ($key === '') // Empty notation will return the value as array + { + break; + } + + if (isset($value[$key])) + { + $value = $value[$key]; + } + else + { + return NULL; + } + } + } + else + { + return NULL; + } + + return ($xss_clean === TRUE) + ? $this->security->xss_clean($value) + : $value; + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the GET array + * + * @param mixed $index Index for item to be fetched from $_GET + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed + */ + public function get($index = NULL, $xss_clean = NULL) + { + return $this->_fetch_from_array($_GET, $index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the POST array + * + * @param mixed $index Index for item to be fetched from $_POST + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed + */ + public function post($index = NULL, $xss_clean = NULL) + { + return $this->_fetch_from_array($_POST, $index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from POST data with fallback to GET + * + * @param string $index Index for item to be fetched from $_POST or $_GET + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed + */ + public function post_get($index, $xss_clean = NULL) + { + return isset($_POST[$index]) + ? $this->post($index, $xss_clean) + : $this->get($index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from GET data with fallback to POST + * + * @param string $index Index for item to be fetched from $_GET or $_POST + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed + */ + public function get_post($index, $xss_clean = NULL) + { + return isset($_GET[$index]) + ? $this->get($index, $xss_clean) + : $this->post($index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the COOKIE array + * + * @param mixed $index Index for item to be fetched from $_COOKIE + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed + */ + public function cookie($index = NULL, $xss_clean = NULL) + { + return $this->_fetch_from_array($_COOKIE, $index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the SERVER array + * + * @param mixed $index Index for item to be fetched from $_SERVER + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed + */ + public function server($index, $xss_clean = NULL) + { + return $this->_fetch_from_array($_SERVER, $index, $xss_clean); + } + + // ------------------------------------------------------------------------ + + /** + * Fetch an item from the php://input stream + * + * Useful when you need to access PUT, DELETE or PATCH request data. + * + * @param string $index Index for item to be fetched + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed + */ + public function input_stream($index = NULL, $xss_clean = NULL) + { + // Prior to PHP 5.6, the input stream can only be read once, + // so we'll need to check if we have already done that first. + if ( ! is_array($this->_input_stream)) + { + // $this->raw_input_stream will trigger __get(). + parse_str($this->raw_input_stream, $this->_input_stream); + is_array($this->_input_stream) OR $this->_input_stream = array(); + } + + return $this->_fetch_from_array($this->_input_stream, $index, $xss_clean); + } + + // ------------------------------------------------------------------------ + + /** + * Set cookie + * + * Accepts an arbitrary number of parameters (up to 7) or an associative + * array in the first parameter containing all the values. + * + * @param string|mixed[] $name Cookie name or an array containing parameters + * @param string $value Cookie value + * @param int $expire Cookie expiration time in seconds + * @param string $domain Cookie domain (e.g.: '.yourdomain.com') + * @param string $path Cookie path (default: '/') + * @param string $prefix Cookie name prefix + * @param bool $secure Whether to only transfer cookies via SSL + * @param bool $httponly Whether to only makes the cookie accessible via HTTP (no javascript) + * @return void + */ + public function set_cookie($name, $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE, $httponly = FALSE) + { + if (is_array($name)) + { + // always leave 'name' in last place, as the loop will break otherwise, due to $$item + foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'httponly', 'name') as $item) + { + if (isset($name[$item])) + { + $$item = $name[$item]; + } + } + } + + if ($prefix === '' && config_item('cookie_prefix') !== '') + { + $prefix = config_item('cookie_prefix'); + } + + if ($domain == '' && config_item('cookie_domain') != '') + { + $domain = config_item('cookie_domain'); + } + + if ($path === '/' && config_item('cookie_path') !== '/') + { + $path = config_item('cookie_path'); + } + + if ($secure === FALSE && config_item('cookie_secure') === TRUE) + { + $secure = config_item('cookie_secure'); + } + + if ($httponly === FALSE && config_item('cookie_httponly') !== FALSE) + { + $httponly = config_item('cookie_httponly'); + } + + if ( ! is_numeric($expire)) + { + $expire = time() - 86500; + } + else + { + $expire = ($expire > 0) ? time() + $expire : 0; + } + + setcookie($prefix.$name, $value, $expire, $path, $domain, $secure, $httponly); + } + + // -------------------------------------------------------------------- + + /** + * Fetch the IP Address + * + * Determines and validates the visitor's IP address. + * + * @return string IP address + */ + public function ip_address() + { + if ($this->ip_address !== FALSE) + { + return $this->ip_address; + } + + $proxy_ips = config_item('proxy_ips'); + if ( ! empty($proxy_ips) && ! is_array($proxy_ips)) + { + $proxy_ips = explode(',', str_replace(' ', '', $proxy_ips)); + } + + $this->ip_address = $this->server('REMOTE_ADDR'); + + if ($proxy_ips) + { + foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header) + { + if (($spoof = $this->server($header)) !== NULL) + { + // Some proxies typically list the whole chain of IP + // addresses through which the client has reached us. + // e.g. client_ip, proxy_ip1, proxy_ip2, etc. + sscanf($spoof, '%[^,]', $spoof); + + if ( ! $this->valid_ip($spoof)) + { + $spoof = NULL; + } + else + { + break; + } + } + } + + if ($spoof) + { + for ($i = 0, $c = count($proxy_ips); $i < $c; $i++) + { + // Check if we have an IP address or a subnet + if (strpos($proxy_ips[$i], '/') === FALSE) + { + // An IP address (and not a subnet) is specified. + // We can compare right away. + if ($proxy_ips[$i] === $this->ip_address) + { + $this->ip_address = $spoof; + break; + } + + continue; + } + + // We have a subnet ... now the heavy lifting begins + isset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.'; + + // If the proxy entry doesn't match the IP protocol - skip it + if (strpos($proxy_ips[$i], $separator) === FALSE) + { + continue; + } + + // Convert the REMOTE_ADDR IP address to binary, if needed + if ( ! isset($ip, $sprintf)) + { + if ($separator === ':') + { + // Make sure we're have the "full" IPv6 format + $ip = explode(':', + str_replace('::', + str_repeat(':', 9 - substr_count($this->ip_address, ':')), + $this->ip_address + ) + ); + + for ($j = 0; $j < 8; $j++) + { + $ip[$j] = intval($ip[$j], 16); + } + + $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b'; + } + else + { + $ip = explode('.', $this->ip_address); + $sprintf = '%08b%08b%08b%08b'; + } + + $ip = vsprintf($sprintf, $ip); + } + + // Split the netmask length off the network address + sscanf($proxy_ips[$i], '%[^/]/%d', $netaddr, $masklen); + + // Again, an IPv6 address is most likely in a compressed form + if ($separator === ':') + { + $netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr)); + for ($i = 0; $i < 8; $i++) + { + $netaddr[$i] = intval($netaddr[$i], 16); + } + } + else + { + $netaddr = explode('.', $netaddr); + } + + // Convert to binary and finally compare + if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0) + { + $this->ip_address = $spoof; + break; + } + } + } + } + + if ( ! $this->valid_ip($this->ip_address)) + { + return $this->ip_address = '0.0.0.0'; + } + + return $this->ip_address; + } + + // -------------------------------------------------------------------- + + /** + * Validate IP Address + * + * @param string $ip IP address + * @param string $which IP protocol: 'ipv4' or 'ipv6' + * @return bool + */ + public function valid_ip($ip, $which = '') + { + switch (strtolower($which)) + { + case 'ipv4': + $which = FILTER_FLAG_IPV4; + break; + case 'ipv6': + $which = FILTER_FLAG_IPV6; + break; + default: + $which = NULL; + break; + } + + return (bool) filter_var($ip, FILTER_VALIDATE_IP, $which); + } + + // -------------------------------------------------------------------- + + /** + * Fetch User Agent string + * + * @return string|null User Agent string or NULL if it doesn't exist + */ + public function user_agent($xss_clean = NULL) + { + return $this->_fetch_from_array($_SERVER, 'HTTP_USER_AGENT', $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Sanitize Globals + * + * Internal method serving for the following purposes: + * + * - Unsets $_GET data, if query strings are not enabled + * - Cleans POST, COOKIE and SERVER data + * - Standardizes newline characters to PHP_EOL + * + * @return void + */ + protected function _sanitize_globals() + { + // Is $_GET data allowed? If not we'll set the $_GET to an empty array + if ($this->_allow_get_array === FALSE) + { + $_GET = array(); + } + elseif (is_array($_GET)) + { + foreach ($_GET as $key => $val) + { + $_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); + } + } + + // Clean $_POST Data + if (is_array($_POST)) + { + foreach ($_POST as $key => $val) + { + $_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); + } + } + + // Clean $_COOKIE Data + if (is_array($_COOKIE)) + { + // Also get rid of specially treated cookies that might be set by a server + // or silly application, that are of no use to a CI application anyway + // but that when present will trip our 'Disallowed Key Characters' alarm + // http://www.ietf.org/rfc/rfc2109.txt + // note that the key names below are single quoted strings, and are not PHP variables + unset( + $_COOKIE['$Version'], + $_COOKIE['$Path'], + $_COOKIE['$Domain'] + ); + + foreach ($_COOKIE as $key => $val) + { + if (($cookie_key = $this->_clean_input_keys($key)) !== FALSE) + { + $_COOKIE[$cookie_key] = $this->_clean_input_data($val); + } + else + { + unset($_COOKIE[$key]); + } + } + } + + // Sanitize PHP_SELF + $_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']); + + log_message('debug', 'Global POST, GET and COOKIE data sanitized'); + } + + // -------------------------------------------------------------------- + + /** + * Clean Input Data + * + * Internal method that aids in escaping data and + * standardizing newline characters to PHP_EOL. + * + * @param string|string[] $str Input string(s) + * @return string + */ + protected function _clean_input_data($str) + { + if (is_array($str)) + { + $new_array = array(); + foreach (array_keys($str) as $key) + { + $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($str[$key]); + } + return $new_array; + } + + /* We strip slashes if magic quotes is on to keep things consistent + + NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and + it will probably not exist in future versions at all. + */ + if ( ! is_php('5.4') && get_magic_quotes_gpc()) + { + $str = stripslashes($str); + } + + // Clean UTF-8 if supported + if (UTF8_ENABLED === TRUE) + { + $str = $this->uni->clean_string($str); + } + + // Remove control characters + $str = remove_invisible_characters($str, FALSE); + + // Standardize newlines if needed + if ($this->_standardize_newlines === TRUE) + { + return preg_replace('/(?:\r\n|[\r\n])/', PHP_EOL, $str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Clean Keys + * + * Internal method that helps to prevent malicious users + * from trying to exploit keys we make sure that keys are + * only named with alpha-numeric text and a few other items. + * + * @param string $str Input string + * @param bool $fatal Whether to terminate script exection + * or to return FALSE if an invalid + * key is encountered + * @return string|bool + */ + protected function _clean_input_keys($str, $fatal = TRUE) + { + if ( ! preg_match('/^[a-z0-9:_\/|-]+$/i', $str)) + { + if ($fatal === TRUE) + { + return FALSE; + } + else + { + set_status_header(503); + echo 'Disallowed Key Characters.'; + exit(7); // EXIT_USER_INPUT + } + } + + // Clean UTF-8 if supported + if (UTF8_ENABLED === TRUE) + { + return $this->uni->clean_string($str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Request Headers + * + * @param bool $xss_clean Whether to apply XSS filtering + * @return array + */ + public function request_headers($xss_clean = FALSE) + { + // If header is already defined, return it immediately + if ( ! empty($this->headers)) + { + return $this->headers; + } + + // In Apache, you can simply call apache_request_headers() + if (function_exists('apache_request_headers')) + { + return $this->headers = apache_request_headers(); + } + + $this->headers['Content-Type'] = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : @getenv('CONTENT_TYPE'); + + foreach ($_SERVER as $key => $val) + { + if (sscanf($key, 'HTTP_%s', $header) === 1) + { + // take SOME_HEADER and turn it into Some-Header + $header = str_replace('_', ' ', strtolower($header)); + $header = str_replace(' ', '-', ucwords($header)); + + $this->headers[$header] = $this->_fetch_from_array($_SERVER, $key, $xss_clean); + } + } + + return $this->headers; + } + + // -------------------------------------------------------------------- + + /** + * Get Request Header + * + * Returns the value of a single member of the headers class member + * + * @param string $index Header name + * @param bool $xss_clean Whether to apply XSS filtering + * @return string|null The requested header on success or NULL on failure + */ + public function get_request_header($index, $xss_clean = FALSE) + { + static $headers; + + if ( ! isset($headers)) + { + empty($this->headers) && $this->request_headers(); + foreach ($this->headers as $key => $value) + { + $headers[strtolower($key)] = $value; + } + } + + $index = strtolower($index); + + if ( ! isset($headers[$index])) + { + return NULL; + } + + return ($xss_clean === TRUE) + ? $this->security->xss_clean($headers[$index]) + : $headers[$index]; + } + + // -------------------------------------------------------------------- + + /** + * Is AJAX request? + * + * Test to see if a request contains the HTTP_X_REQUESTED_WITH header. + * + * @return bool + */ + public function is_ajax_request() + { + return ( ! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'); + } + + // -------------------------------------------------------------------- + + /** + * Is CLI request? + * + * Test to see if a request was made from the command line. + * + * @deprecated 3.0.0 Use is_cli() instead + * @return bool + */ + public function is_cli_request() + { + return is_cli(); + } + + // -------------------------------------------------------------------- + + /** + * Get Request Method + * + * Return the request method + * + * @param bool $upper Whether to return in upper or lower case + * (default: FALSE) + * @return string + */ + public function method($upper = FALSE) + { + return ($upper) + ? strtoupper($this->server('REQUEST_METHOD')) + : strtolower($this->server('REQUEST_METHOD')); + } + + // ------------------------------------------------------------------------ + + /** + * Magic __get() + * + * Allows read access to protected properties + * + * @param string $name + * @return mixed + */ + public function __get($name) + { + if ($name === 'raw_input_stream') + { + isset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input'); + return $this->_raw_input_stream; + } + elseif ($name === 'ip_address') + { + return $this->ip_address; + } + } + +} diff --git a/codeigniter-3.0/system/core/Lang.php b/ci-3.0/system/core/Lang.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/core/Lang.php rename to ci-3.0/system/core/Lang.php diff --git a/ci-3.0/system/core/Loader.php b/ci-3.0/system/core/Loader.php new file mode 100755 index 000000000..18e4c5287 --- /dev/null +++ b/ci-3.0/system/core/Loader.php @@ -0,0 +1,1395 @@ + TRUE); + + /** + * List of paths to load libraries from + * + * @var array + */ + protected $_ci_library_paths = array(APPPATH, BASEPATH); + + /** + * List of paths to load models from + * + * @var array + */ + protected $_ci_model_paths = array(APPPATH); + + /** + * List of paths to load helpers from + * + * @var array + */ + protected $_ci_helper_paths = array(APPPATH, BASEPATH); + + /** + * List of cached variables + * + * @var array + */ + protected $_ci_cached_vars = array(); + + /** + * List of loaded classes + * + * @var array + */ + protected $_ci_classes = array(); + + /** + * List of loaded models + * + * @var array + */ + protected $_ci_models = array(); + + /** + * List of loaded helpers + * + * @var array + */ + protected $_ci_helpers = array(); + + /** + * List of class name mappings + * + * @var array + */ + protected $_ci_varmap = array( + 'unit_test' => 'unit', + 'user_agent' => 'agent' + ); + + // -------------------------------------------------------------------- + + /** + * Class constructor + * + * Sets component load paths, gets the initial output buffering level. + * + * @return void + */ + public function __construct() + { + $this->_ci_ob_level = ob_get_level(); + $this->_ci_classes =& is_loaded(); + + log_message('info', 'Loader Class Initialized'); + } + + // -------------------------------------------------------------------- + + /** + * Initializer + * + * @todo Figure out a way to move this to the constructor + * without breaking *package_path*() methods. + * @uses CI_Loader::_ci_autoloader() + * @used-by CI_Controller::__construct() + * @return void + */ + public function initialize() + { + $this->_ci_autoloader(); + } + + // -------------------------------------------------------------------- + + /** + * Is Loaded + * + * A utility method to test if a class is in the self::$_ci_classes array. + * + * @used-by Mainly used by Form Helper function _get_validation_object(). + * + * @param string $class Class name to check for + * @return string|bool Class object name if loaded or FALSE + */ + public function is_loaded($class) + { + return array_search(ucfirst($class), $this->_ci_classes, TRUE); + } + + // -------------------------------------------------------------------- + + /** + * Library Loader + * + * Loads and instantiates libraries. + * Designed to be called from application controllers. + * + * @param string $library Library name + * @param array $params Optional parameters to pass to the library class constructor + * @param string $object_name An optional object name to assign to + * @return object + */ + public function library($library, $params = NULL, $object_name = NULL) + { + if (empty($library)) + { + return $this; + } + elseif (is_array($library)) + { + foreach ($library as $key => $value) + { + if (is_int($key)) + { + $this->library($value, $params); + } + else + { + $this->library($key, $params, $value); + } + } + + return $this; + } + + if ($params !== NULL && ! is_array($params)) + { + $params = NULL; + } + + $this->_ci_load_library($library, $params, $object_name); + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Model Loader + * + * Loads and instantiates models. + * + * @param string $model Model name + * @param string $name An optional object name to assign to + * @param bool $db_conn An optional database connection configuration to initialize + * @return object + */ + public function model($model, $name = '', $db_conn = FALSE) + { + if (empty($model)) + { + return $this; + } + elseif (is_array($model)) + { + foreach ($model as $key => $value) + { + is_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn); + } + + return $this; + } + + $path = ''; + + // Is the model in a sub-folder? If so, parse out the filename and path. + if (($last_slash = strrpos($model, '/')) !== FALSE) + { + // The path is in front of the last slash + $path = substr($model, 0, ++$last_slash); + + // And the model name behind it + $model = substr($model, $last_slash); + } + + if (empty($name)) + { + $name = $model; + } + + if (in_array($name, $this->_ci_models, TRUE)) + { + return $this; + } + + $CI =& get_instance(); + if (isset($CI->$name)) + { + throw new RuntimeException('The model name you are loading is the name of a resource that is already being used: '.$name); + } + + if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE)) + { + if ($db_conn === TRUE) + { + $db_conn = ''; + } + + $this->database($db_conn, FALSE, TRUE); + } + + if ( ! class_exists('CI_Model', FALSE)) + { + load_class('Model', 'core'); + } + + $model = ucfirst($model); + if ( ! class_exists($model)) + { + foreach ($this->_ci_model_paths as $mod_path) + { + if ( ! file_exists($mod_path.'models/'.$path.$model.'.php')) + { + continue; + } + + require_once($mod_path.'models/'.$path.$model.'.php'); + if ( ! class_exists($model, FALSE)) + { + throw new RuntimeException($mod_path."models/".$path.$model.".php exists, but doesn't declare class ".$model); + } + + break; + } + + if ( ! class_exists($model, FALSE)) + { + throw new RuntimeException('Unable to locate the model you have specified: '.$model); + } + } + elseif ( ! is_subclass_of($model, 'CI_Model')) + { + throw new RuntimeException("Class ".$model." already exists and doesn't extend CI_Model"); + } + + $this->_ci_models[] = $name; + $CI->$name = new $model(); + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Database Loader + * + * @param mixed $params Database configuration options + * @param bool $return Whether to return the database object + * @param bool $query_builder Whether to enable Query Builder + * (overrides the configuration setting) + * + * @return object|bool Database object if $return is set to TRUE, + * FALSE on failure, CI_Loader instance in any other case + */ + public function database($params = '', $return = FALSE, $query_builder = NULL) + { + // Grab the super object + $CI =& get_instance(); + + // Do we even need to load the database class? + if ($return === FALSE && $query_builder === NULL && isset($CI->db) && is_object($CI->db) && ! empty($CI->db->conn_id)) + { + return FALSE; + } + + require_once(BASEPATH.'database/DB.php'); + + if ($return === TRUE) + { + return DB($params, $query_builder); + } + + // Initialize the db variable. Needed to prevent + // reference errors with some configurations + $CI->db = ''; + + // Load the DB class + $CI->db =& DB($params, $query_builder); + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Load the Database Utilities Class + * + * @param object $db Database object + * @param bool $return Whether to return the DB Utilities class object or not + * @return object + */ + public function dbutil($db = NULL, $return = FALSE) + { + $CI =& get_instance(); + + if ( ! is_object($db) OR ! ($db instanceof CI_DB)) + { + class_exists('CI_DB', FALSE) OR $this->database(); + $db =& $CI->db; + } + + require_once(BASEPATH.'database/DB_utility.php'); + require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_utility.php'); + $class = 'CI_DB_'.$db->dbdriver.'_utility'; + + if ($return === TRUE) + { + return new $class($db); + } + + $CI->dbutil = new $class($db); + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Load the Database Forge Class + * + * @param object $db Database object + * @param bool $return Whether to return the DB Forge class object or not + * @return object + */ + public function dbforge($db = NULL, $return = FALSE) + { + $CI =& get_instance(); + if ( ! is_object($db) OR ! ($db instanceof CI_DB)) + { + class_exists('CI_DB', FALSE) OR $this->database(); + $db =& $CI->db; + } + + require_once(BASEPATH.'database/DB_forge.php'); + require_once(BASEPATH.'database/drivers/'.$db->dbdriver.'/'.$db->dbdriver.'_forge.php'); + + if ( ! empty($db->subdriver)) + { + $driver_path = BASEPATH.'database/drivers/'.$db->dbdriver.'/subdrivers/'.$db->dbdriver.'_'.$db->subdriver.'_forge.php'; + if (file_exists($driver_path)) + { + require_once($driver_path); + $class = 'CI_DB_'.$db->dbdriver.'_'.$db->subdriver.'_forge'; + } + } + else + { + $class = 'CI_DB_'.$db->dbdriver.'_forge'; + } + + if ($return === TRUE) + { + return new $class($db); + } + + $CI->dbforge = new $class($db); + return $this; + } + + // -------------------------------------------------------------------- + + /** + * View Loader + * + * Loads "view" files. + * + * @param string $view View name + * @param array $vars An associative array of data + * to be extracted for use in the view + * @param bool $return Whether to return the view output + * or leave it to the Output class + * @return object|string + */ + public function view($view, $vars = array(), $return = FALSE) + { + return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return)); + } + + // -------------------------------------------------------------------- + + /** + * Generic File Loader + * + * @param string $path File path + * @param bool $return Whether to return the file output + * @return object|string + */ + public function file($path, $return = FALSE) + { + return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return)); + } + + // -------------------------------------------------------------------- + + /** + * Set Variables + * + * Once variables are set they become available within + * the controller class and its "view" files. + * + * @param array|object|string $vars + * An associative array or object containing values + * to be set, or a value's name if string + * @param string $val Value to set, only used if $vars is a string + * @return object + */ + public function vars($vars, $val = '') + { + if (is_string($vars)) + { + $vars = array($vars => $val); + } + + $vars = $this->_ci_object_to_array($vars); + + if (is_array($vars) && count($vars) > 0) + { + foreach ($vars as $key => $val) + { + $this->_ci_cached_vars[$key] = $val; + } + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Clear Cached Variables + * + * Clears the cached variables. + * + * @return CI_Loader + */ + public function clear_vars() + { + $this->_ci_cached_vars = array(); + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Get Variable + * + * Check if a variable is set and retrieve it. + * + * @param string $key Variable name + * @return mixed The variable or NULL if not found + */ + public function get_var($key) + { + return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL; + } + + // -------------------------------------------------------------------- + + /** + * Get Variables + * + * Retrieves all loaded variables. + * + * @return array + */ + public function get_vars() + { + return $this->_ci_cached_vars; + } + + // -------------------------------------------------------------------- + + /** + * Helper Loader + * + * @param string|string[] $helpers Helper name(s) + * @return object + */ + public function helper($helpers = array()) + { + foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper) + { + if (isset($this->_ci_helpers[$helper])) + { + continue; + } + + // Is this a helper extension request? + $ext_helper = config_item('subclass_prefix').$helper; + $ext_loaded = FALSE; + foreach ($this->_ci_helper_paths as $path) + { + if (file_exists($path.'helpers/'.$ext_helper.'.php')) + { + include_once($path.'helpers/'.$ext_helper.'.php'); + $ext_loaded = TRUE; + } + } + + // If we have loaded extensions - check if the base one is here + if ($ext_loaded === TRUE) + { + $base_helper = BASEPATH.'helpers/'.$helper.'.php'; + if ( ! file_exists($base_helper)) + { + show_error('Unable to load the requested file: helpers/'.$helper.'.php'); + } + + include_once($base_helper); + $this->_ci_helpers[$helper] = TRUE; + log_message('info', 'Helper loaded: '.$helper); + continue; + } + + // No extensions found ... try loading regular helpers and/or overrides + foreach ($this->_ci_helper_paths as $path) + { + if (file_exists($path.'helpers/'.$helper.'.php')) + { + include_once($path.'helpers/'.$helper.'.php'); + + $this->_ci_helpers[$helper] = TRUE; + log_message('info', 'Helper loaded: '.$helper); + break; + } + } + + // unable to load the helper + if ( ! isset($this->_ci_helpers[$helper])) + { + show_error('Unable to load the requested file: helpers/'.$helper.'.php'); + } + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Load Helpers + * + * An alias for the helper() method in case the developer has + * written the plural form of it. + * + * @uses CI_Loader::helper() + * @param string|string[] $helpers Helper name(s) + * @return object + */ + public function helpers($helpers = array()) + { + return $this->helper($helpers); + } + + // -------------------------------------------------------------------- + + /** + * Language Loader + * + * Loads language files. + * + * @param string|string[] $files List of language file names to load + * @param string Language name + * @return object + */ + public function language($files, $lang = '') + { + get_instance()->lang->load($files, $lang); + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Config Loader + * + * Loads a config file (an alias for CI_Config::load()). + * + * @uses CI_Config::load() + * @param string $file Configuration file name + * @param bool $use_sections Whether configuration values should be loaded into their own section + * @param bool $fail_gracefully Whether to just return FALSE or display an error message + * @return bool TRUE if the file was loaded correctly or FALSE on failure + */ + public function config($file, $use_sections = FALSE, $fail_gracefully = FALSE) + { + return get_instance()->config->load($file, $use_sections, $fail_gracefully); + } + + // -------------------------------------------------------------------- + + /** + * Driver Loader + * + * Loads a driver library. + * + * @param string|string[] $library Driver name(s) + * @param array $params Optional parameters to pass to the driver + * @param string $object_name An optional object name to assign to + * + * @return object|bool Object or FALSE on failure if $library is a string + * and $object_name is set. CI_Loader instance otherwise. + */ + public function driver($library, $params = NULL, $object_name = NULL) + { + if (is_array($library)) + { + foreach ($library as $driver) + { + $this->driver($driver); + } + + return $this; + } + elseif (empty($library)) + { + return FALSE; + } + + if ( ! class_exists('CI_Driver_Library', FALSE)) + { + // We aren't instantiating an object here, just making the base class available + require BASEPATH.'libraries/Driver.php'; + } + + // We can save the loader some time since Drivers will *always* be in a subfolder, + // and typically identically named to the library + if ( ! strpos($library, '/')) + { + $library = ucfirst($library).'/'.$library; + } + + return $this->library($library, $params, $object_name); + } + + // -------------------------------------------------------------------- + + /** + * Add Package Path + * + * Prepends a parent path to the library, model, helper and config + * path arrays. + * + * @see CI_Loader::$_ci_library_paths + * @see CI_Loader::$_ci_model_paths + * @see CI_Loader::$_ci_helper_paths + * @see CI_Config::$_config_paths + * + * @param string $path Path to add + * @param bool $view_cascade (default: TRUE) + * @return object + */ + public function add_package_path($path, $view_cascade = TRUE) + { + $path = rtrim($path, '/').'/'; + + array_unshift($this->_ci_library_paths, $path); + array_unshift($this->_ci_model_paths, $path); + array_unshift($this->_ci_helper_paths, $path); + + $this->_ci_view_paths = array($path.'views/' => $view_cascade) + $this->_ci_view_paths; + + // Add config file path + $config =& $this->_ci_get_component('config'); + $config->_config_paths[] = $path; + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Get Package Paths + * + * Return a list of all package paths. + * + * @param bool $include_base Whether to include BASEPATH (default: FALSE) + * @return array + */ + public function get_package_paths($include_base = FALSE) + { + return ($include_base === TRUE) ? $this->_ci_library_paths : $this->_ci_model_paths; + } + + // -------------------------------------------------------------------- + + /** + * Remove Package Path + * + * Remove a path from the library, model, helper and/or config + * path arrays if it exists. If no path is provided, the most recently + * added path will be removed removed. + * + * @param string $path Path to remove + * @return object + */ + public function remove_package_path($path = '') + { + $config =& $this->_ci_get_component('config'); + + if ($path === '') + { + array_shift($this->_ci_library_paths); + array_shift($this->_ci_model_paths); + array_shift($this->_ci_helper_paths); + array_shift($this->_ci_view_paths); + array_pop($config->_config_paths); + } + else + { + $path = rtrim($path, '/').'/'; + foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var) + { + if (($key = array_search($path, $this->{$var})) !== FALSE) + { + unset($this->{$var}[$key]); + } + } + + if (isset($this->_ci_view_paths[$path.'views/'])) + { + unset($this->_ci_view_paths[$path.'views/']); + } + + if (($key = array_search($path, $config->_config_paths)) !== FALSE) + { + unset($config->_config_paths[$key]); + } + } + + // make sure the application default paths are still in the array + $this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH))); + $this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH))); + $this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH))); + $this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE)); + $config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH))); + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Internal CI Data Loader + * + * Used to load views and files. + * + * Variables are prefixed with _ci_ to avoid symbol collision with + * variables made available to view files. + * + * @used-by CI_Loader::view() + * @used-by CI_Loader::file() + * @param array $_ci_data Data to load + * @return object + */ + protected function _ci_load($_ci_data) + { + // Set the default data variables + foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val) + { + $$_ci_val = isset($_ci_data[$_ci_val]) ? $_ci_data[$_ci_val] : FALSE; + } + + $file_exists = FALSE; + + // Set the path to the requested file + if (is_string($_ci_path) && $_ci_path !== '') + { + $_ci_x = explode('/', $_ci_path); + $_ci_file = end($_ci_x); + } + else + { + $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION); + $_ci_file = ($_ci_ext === '') ? $_ci_view.'.php' : $_ci_view; + + foreach ($this->_ci_view_paths as $_ci_view_file => $cascade) + { + if (file_exists($_ci_view_file.$_ci_file)) + { + $_ci_path = $_ci_view_file.$_ci_file; + $file_exists = TRUE; + break; + } + + if ( ! $cascade) + { + break; + } + } + } + + if ( ! $file_exists && ! file_exists($_ci_path)) + { + show_error('Unable to load the requested file: '.$_ci_file); + } + + // This allows anything loaded using $this->load (views, files, etc.) + // to become accessible from within the Controller and Model functions. + $_ci_CI =& get_instance(); + foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var) + { + if ( ! isset($this->$_ci_key)) + { + $this->$_ci_key =& $_ci_CI->$_ci_key; + } + } + + /* + * Extract and cache variables + * + * You can either set variables using the dedicated $this->load->vars() + * function or via the second parameter of this function. We'll merge + * the two types and cache them so that views that are embedded within + * other views can have access to these variables. + */ + if (is_array($_ci_vars)) + { + $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars); + } + extract($this->_ci_cached_vars); + + /* + * Buffer the output + * + * We buffer the output for two reasons: + * 1. Speed. You get a significant speed boost. + * 2. So that the final rendered template can be post-processed by + * the output class. Why do we need post processing? For one thing, + * in order to show the elapsed page load time. Unless we can + * intercept the content right before it's sent to the browser and + * then stop the timer it won't be accurate. + */ + ob_start(); + + // If the PHP installation does not support short tags we'll + // do a little string replacement, changing the short tags + // to standard PHP echo statements. + if ( ! is_php('5.4') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE) + { + echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace(' $this->_ci_ob_level + 1) + { + ob_end_flush(); + } + else + { + $_ci_CI->output->append_output(ob_get_contents()); + @ob_end_clean(); + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Internal CI Library Loader + * + * @used-by CI_Loader::library() + * @uses CI_Loader::_ci_init_library() + * + * @param string $class Class name to load + * @param mixed $params Optional parameters to pass to the class constructor + * @param string $object_name Optional object name to assign to + * @return void + */ + protected function _ci_load_library($class, $params = NULL, $object_name = NULL) + { + // Get the class name, and while we're at it trim any slashes. + // The directory path can be included as part of the class name, + // but we don't want a leading slash + $class = str_replace('.php', '', trim($class, '/')); + + // Was the path included with the class name? + // We look for a slash to determine this + if (($last_slash = strrpos($class, '/')) !== FALSE) + { + // Extract the path + $subdir = substr($class, 0, ++$last_slash); + + // Get the filename from the path + $class = substr($class, $last_slash); + } + else + { + $subdir = ''; + } + + $class = ucfirst($class); + + // Is this a stock library? There are a few special conditions if so ... + if (file_exists(BASEPATH.'libraries/'.$subdir.$class.'.php')) + { + return $this->_ci_load_stock_library($class, $subdir, $params, $object_name); + } + + // Let's search for the requested library file and load it. + foreach ($this->_ci_library_paths as $path) + { + // BASEPATH has already been checked for + if ($path === BASEPATH) + { + continue; + } + + $filepath = $path.'libraries/'.$subdir.$class.'.php'; + + // Safety: Was the class already loaded by a previous call? + if (class_exists($class, FALSE)) + { + // Before we deem this to be a duplicate request, let's see + // if a custom object name is being supplied. If so, we'll + // return a new instance of the object + if ($object_name !== NULL) + { + $CI =& get_instance(); + if ( ! isset($CI->$object_name)) + { + return $this->_ci_init_library($class, '', $params, $object_name); + } + } + + log_message('debug', $class.' class already loaded. Second attempt ignored.'); + return; + } + // Does the file exist? No? Bummer... + elseif ( ! file_exists($filepath)) + { + continue; + } + + include_once($filepath); + return $this->_ci_init_library($class, '', $params, $object_name); + } + + // One last attempt. Maybe the library is in a subdirectory, but it wasn't specified? + if ($subdir === '') + { + return $this->_ci_load_library($class.'/'.$class, $params, $object_name); + } + + // If we got this far we were unable to find the requested class. + log_message('error', 'Unable to load the requested class: '.$class); + show_error('Unable to load the requested class: '.$class); + } + + // -------------------------------------------------------------------- + + /** + * Internal CI Stock Library Loader + * + * @used-by CI_Loader::_ci_load_library() + * @uses CI_Loader::_ci_init_library() + * + * @param string $library Library name to load + * @param string $file_path Path to the library filename, relative to libraries/ + * @param mixed $params Optional parameters to pass to the class constructor + * @param string $object_name Optional object name to assign to + * @return void + */ + protected function _ci_load_stock_library($library_name, $file_path, $params, $object_name) + { + $prefix = 'CI_'; + + if (class_exists($prefix.$library_name, FALSE)) + { + if (class_exists(config_item('subclass_prefix').$library_name, FALSE)) + { + $prefix = config_item('subclass_prefix'); + } + + // Before we deem this to be a duplicate request, let's see + // if a custom object name is being supplied. If so, we'll + // return a new instance of the object + if ($object_name !== NULL) + { + $CI =& get_instance(); + if ( ! isset($CI->$object_name)) + { + return $this->_ci_init_library($library_name, $prefix, $params, $object_name); + } + } + + log_message('debug', $library_name.' class already loaded. Second attempt ignored.'); + return; + } + + $paths = $this->_ci_library_paths; + array_pop($paths); // BASEPATH + array_pop($paths); // APPPATH (needs to be the first path checked) + array_unshift($paths, APPPATH); + + foreach ($paths as $path) + { + if (file_exists($path = $path.'libraries/'.$file_path.$library_name.'.php')) + { + // Override + include_once($path); + if (class_exists($prefix.$library_name, FALSE)) + { + return $this->_ci_init_library($library_name, $prefix, $params, $object_name); + } + else + { + log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name); + } + } + } + + include_once(BASEPATH.'libraries/'.$file_path.$library_name.'.php'); + + // Check for extensions + $subclass = config_item('subclass_prefix').$library_name; + foreach ($paths as $path) + { + if (file_exists($path = $path.'libraries/'.$file_path.$subclass.'.php')) + { + include_once($path); + if (class_exists($subclass, FALSE)) + { + $prefix = config_item('subclass_prefix'); + break; + } + else + { + log_message('debug', $path.' exists, but does not declare '.$subclass); + } + } + } + + return $this->_ci_init_library($library_name, $prefix, $params, $object_name); + } + + // -------------------------------------------------------------------- + + /** + * Internal CI Library Instantiator + * + * @used-by CI_Loader::_ci_load_stock_library() + * @used-by CI_Loader::_ci_load_library() + * + * @param string $class Class name + * @param string $prefix Class name prefix + * @param array|null|bool $config Optional configuration to pass to the class constructor: + * FALSE to skip; + * NULL to search in config paths; + * array containing configuration data + * @param string $object_name Optional object name to assign to + * @return void + */ + protected function _ci_init_library($class, $prefix, $config = FALSE, $object_name = NULL) + { + // Is there an associated config file for this class? Note: these should always be lowercase + if ($config === NULL) + { + // Fetch the config paths containing any package paths + $config_component = $this->_ci_get_component('config'); + + if (is_array($config_component->_config_paths)) + { + $found = FALSE; + foreach ($config_component->_config_paths as $path) + { + // We test for both uppercase and lowercase, for servers that + // are case-sensitive with regard to file names. Load global first, + // override with environment next + if (file_exists($path.'config/'.strtolower($class).'.php')) + { + include($path.'config/'.strtolower($class).'.php'); + $found = TRUE; + } + elseif (file_exists($path.'config/'.ucfirst(strtolower($class)).'.php')) + { + include($path.'config/'.ucfirst(strtolower($class)).'.php'); + $found = TRUE; + } + + if (file_exists($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php')) + { + include($path.'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'); + $found = TRUE; + } + elseif (file_exists($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php')) + { + include($path.'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'); + $found = TRUE; + } + + // Break on the first found configuration, thus package + // files are not overridden by default paths + if ($found === TRUE) + { + break; + } + } + } + } + + $class_name = $prefix.$class; + + // Is the class name valid? + if ( ! class_exists($class_name, FALSE)) + { + log_message('error', 'Non-existent class: '.$class_name); + show_error('Non-existent class: '.$class_name); + } + + // Set the variable name we will assign the class to + // Was a custom class name supplied? If so we'll use it + if (empty($object_name)) + { + $object_name = strtolower($class); + if (isset($this->_ci_varmap[$object_name])) + { + $object_name = $this->_ci_varmap[$object_name]; + } + } + + // Don't overwrite existing properties + $CI =& get_instance(); + if (isset($CI->$object_name)) + { + if ($CI->$object_name instanceof $class_name) + { + log_message('debug', $class_name." has already been instantiated as '".$object_name."'. Second attempt aborted."); + return; + } + + show_error("Resource '".$object_name."' already exists and is not a ".$class_name." instance."); + } + + // Save the class name and object name + $this->_ci_classes[$object_name] = $class; + + // Instantiate the class + $CI->$object_name = isset($config) + ? new $class_name($config) + : new $class_name(); + } + + // -------------------------------------------------------------------- + + /** + * CI Autoloader + * + * Loads component listed in the config/autoload.php file. + * + * @used-by CI_Loader::initialize() + * @return void + */ + protected function _ci_autoloader() + { + if (file_exists(APPPATH.'config/autoload.php')) + { + include(APPPATH.'config/autoload.php'); + } + + if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php')) + { + include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'); + } + + if ( ! isset($autoload)) + { + return; + } + + // Autoload packages + if (isset($autoload['packages'])) + { + foreach ($autoload['packages'] as $package_path) + { + $this->add_package_path($package_path); + } + } + + // Load any custom config file + if (count($autoload['config']) > 0) + { + foreach ($autoload['config'] as $val) + { + $this->config($val); + } + } + + // Autoload helpers and languages + foreach (array('helper', 'language') as $type) + { + if (isset($autoload[$type]) && count($autoload[$type]) > 0) + { + $this->$type($autoload[$type]); + } + } + + // Autoload drivers + if (isset($autoload['drivers'])) + { + foreach ($autoload['drivers'] as $item) + { + $this->driver($item); + } + } + + // Load libraries + if (isset($autoload['libraries']) && count($autoload['libraries']) > 0) + { + // Load the database driver. + if (in_array('database', $autoload['libraries'])) + { + $this->database(); + $autoload['libraries'] = array_diff($autoload['libraries'], array('database')); + } + + // Load all other libraries + $this->library($autoload['libraries']); + } + + // Autoload models + if (isset($autoload['model'])) + { + $this->model($autoload['model']); + } + } + + // -------------------------------------------------------------------- + + /** + * CI Object to Array translator + * + * Takes an object as input and converts the class variables to + * an associative array with key/value pairs. + * + * @param object $object Object data to translate + * @return array + */ + protected function _ci_object_to_array($object) + { + return is_object($object) ? get_object_vars($object) : $object; + } + + // -------------------------------------------------------------------- + + /** + * CI Component getter + * + * Get a reference to a specific library or model. + * + * @param string $component Component name + * @return bool + */ + protected function &_ci_get_component($component) + { + $CI =& get_instance(); + return $CI->$component; + } + + // -------------------------------------------------------------------- + + /** + * Prep filename + * + * This function prepares filenames of various items to + * make their loading more reliable. + * + * @param string|string[] $filename Filename(s) + * @param string $extension Filename extension + * @return array + */ + protected function _ci_prep_filename($filename, $extension) + { + if ( ! is_array($filename)) + { + return array(strtolower(str_replace(array($extension, '.php'), '', $filename).$extension)); + } + else + { + foreach ($filename as $key => $val) + { + $filename[$key] = strtolower(str_replace(array($extension, '.php'), '', $val).$extension); + } + + return $filename; + } + } + +} diff --git a/codeigniter-3.0/system/core/Log.php b/ci-3.0/system/core/Log.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/core/Log.php rename to ci-3.0/system/core/Log.php diff --git a/codeigniter-3.0/system/core/Model.php b/ci-3.0/system/core/Model.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/core/Model.php rename to ci-3.0/system/core/Model.php diff --git a/ci-3.0/system/core/Output.php b/ci-3.0/system/core/Output.php new file mode 100755 index 000000000..76c1329d2 --- /dev/null +++ b/ci-3.0/system/core/Output.php @@ -0,0 +1,800 @@ +_zlib_oc = (bool) ini_get('zlib.output_compression'); + $this->_compress_output = ( + $this->_zlib_oc === FALSE + && config_item('compress_output') === TRUE + && extension_loaded('zlib') + ); + + // Get mime types for later + $this->mimes =& get_mimes(); + + log_message('info', 'Output Class Initialized'); + } + + // -------------------------------------------------------------------- + + /** + * Get Output + * + * Returns the current output string. + * + * @return string + */ + public function get_output() + { + return $this->final_output; + } + + // -------------------------------------------------------------------- + + /** + * Set Output + * + * Sets the output string. + * + * @param string $output Output data + * @return CI_Output + */ + public function set_output($output) + { + $this->final_output = $output; + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Append Output + * + * Appends data onto the output string. + * + * @param string $output Data to append + * @return CI_Output + */ + public function append_output($output) + { + $this->final_output .= $output; + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Set Header + * + * Lets you set a server header which will be sent with the final output. + * + * Note: If a file is cached, headers will not be sent. + * @todo We need to figure out how to permit headers to be cached. + * + * @param string $header Header + * @param bool $replace Whether to replace the old header value, if already set + * @return CI_Output + */ + public function set_header($header, $replace = TRUE) + { + // If zlib.output_compression is enabled it will compress the output, + // but it will not modify the content-length header to compensate for + // the reduction, causing the browser to hang waiting for more data. + // We'll just skip content-length in those cases. + if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0) + { + return $this; + } + + $this->headers[] = array($header, $replace); + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Set Content-Type Header + * + * @param string $mime_type Extension of the file we're outputting + * @param string $charset Character set (default: NULL) + * @return CI_Output + */ + public function set_content_type($mime_type, $charset = NULL) + { + if (strpos($mime_type, '/') === FALSE) + { + $extension = ltrim($mime_type, '.'); + + // Is this extension supported? + if (isset($this->mimes[$extension])) + { + $mime_type =& $this->mimes[$extension]; + + if (is_array($mime_type)) + { + $mime_type = current($mime_type); + } + } + } + + $this->mime_type = $mime_type; + + if (empty($charset)) + { + $charset = config_item('charset'); + } + + $header = 'Content-Type: '.$mime_type + .(empty($charset) ? '' : '; charset='.$charset); + + $this->headers[] = array($header, TRUE); + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Get Current Content-Type Header + * + * @return string 'text/html', if not already set + */ + public function get_content_type() + { + for ($i = 0, $c = count($this->headers); $i < $c; $i++) + { + if (sscanf($this->headers[$i][0], 'Content-Type: %[^;]', $content_type) === 1) + { + return $content_type; + } + } + + return 'text/html'; + } + + // -------------------------------------------------------------------- + + /** + * Get Header + * + * @param string $header_name + * @return string + */ + public function get_header($header) + { + // Combine headers already sent with our batched headers + $headers = array_merge( + // We only need [x][0] from our multi-dimensional array + array_map('array_shift', $this->headers), + headers_list() + ); + + if (empty($headers) OR empty($header)) + { + return NULL; + } + + for ($i = 0, $c = count($headers); $i < $c; $i++) + { + if (strncasecmp($header, $headers[$i], $l = strlen($header)) === 0) + { + return trim(substr($headers[$i], $l+1)); + } + } + + return NULL; + } + + // -------------------------------------------------------------------- + + /** + * Set HTTP Status Header + * + * As of version 1.7.2, this is an alias for common function + * set_status_header(). + * + * @param int $code Status code (default: 200) + * @param string $text Optional message + * @return CI_Output + */ + public function set_status_header($code = 200, $text = '') + { + set_status_header($code, $text); + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Enable/disable Profiler + * + * @param bool $val TRUE to enable or FALSE to disable + * @return CI_Output + */ + public function enable_profiler($val = TRUE) + { + $this->enable_profiler = is_bool($val) ? $val : TRUE; + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Set Profiler Sections + * + * Allows override of default/config settings for + * Profiler section display. + * + * @param array $sections Profiler sections + * @return CI_Output + */ + public function set_profiler_sections($sections) + { + if (isset($sections['query_toggle_count'])) + { + $this->_profiler_sections['query_toggle_count'] = (int) $sections['query_toggle_count']; + unset($sections['query_toggle_count']); + } + + foreach ($sections as $section => $enable) + { + $this->_profiler_sections[$section] = ($enable !== FALSE); + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Set Cache + * + * @param int $time Cache expiration time in seconds + * @return CI_Output + */ + public function cache($time) + { + $this->cache_expiration = is_numeric($time) ? $time : 0; + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Display Output + * + * Processes and sends finalized output data to the browser along + * with any server headers and profile data. It also stops benchmark + * timers so the page rendering speed and memory usage can be shown. + * + * Note: All "view" data is automatically put into $this->final_output + * by controller class. + * + * @uses CI_Output::$final_output + * @param string $output Output data override + * @return void + */ + public function _display($output = '') + { + // Note: We use load_class() because we can't use $CI =& get_instance() + // since this function is sometimes called by the caching mechanism, + // which happens before the CI super object is available. + $BM =& load_class('Benchmark', 'core'); + $CFG =& load_class('Config', 'core'); + + // Grab the super object if we can. + if (class_exists('CI_Controller', FALSE)) + { + $CI =& get_instance(); + } + + // -------------------------------------------------------------------- + + // Set the output data + if ($output === '') + { + $output =& $this->final_output; + } + + // -------------------------------------------------------------------- + + // Do we need to write a cache file? Only if the controller does not have its + // own _output() method and we are not dealing with a cache file, which we + // can determine by the existence of the $CI object above + if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output')) + { + $this->_write_cache($output); + } + + // -------------------------------------------------------------------- + + // Parse out the elapsed time and memory usage, + // then swap the pseudo-variables with the data + + $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end'); + + if ($this->parse_exec_vars === TRUE) + { + $memory = round(memory_get_usage() / 1024 / 1024, 2).'MB'; + $output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output); + } + + // -------------------------------------------------------------------- + + // Is compression requested? + if (isset($CI) // This means that we're not serving a cache file, if we were, it would already be compressed + && $this->_compress_output === TRUE + && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) + { + ob_start('ob_gzhandler'); + } + + // -------------------------------------------------------------------- + + // Are there any server headers to send? + if (count($this->headers) > 0) + { + foreach ($this->headers as $header) + { + @header($header[0], $header[1]); + } + } + + // -------------------------------------------------------------------- + + // Does the $CI object exist? + // If not we know we are dealing with a cache file so we'll + // simply echo out the data and exit. + if ( ! isset($CI)) + { + if ($this->_compress_output === TRUE) + { + if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) + { + header('Content-Encoding: gzip'); + header('Content-Length: '.strlen($output)); + } + else + { + // User agent doesn't support gzip compression, + // so we'll have to decompress our cache + $output = gzinflate(substr($output, 10, -8)); + } + } + + echo $output; + log_message('info', 'Final output sent to browser'); + log_message('debug', 'Total execution time: '.$elapsed); + return; + } + + // -------------------------------------------------------------------- + + // Do we need to generate profile data? + // If so, load the Profile class and run it. + if ($this->enable_profiler === TRUE) + { + $CI->load->library('profiler'); + if ( ! empty($this->_profiler_sections)) + { + $CI->profiler->set_sections($this->_profiler_sections); + } + + // If the output data contains closing and tags + // we will remove them and add them back after we insert the profile data + $output = preg_replace('|.*?|is', '', $output, -1, $count).$CI->profiler->run(); + if ($count > 0) + { + $output .= ''; + } + } + + // Does the controller contain a function named _output()? + // If so send the output there. Otherwise, echo it. + if (method_exists($CI, '_output')) + { + $CI->_output($output); + } + else + { + echo $output; // Send it to the browser! + } + + log_message('info', 'Final output sent to browser'); + log_message('debug', 'Total execution time: '.$elapsed); + } + + // -------------------------------------------------------------------- + + /** + * Write Cache + * + * @param string $output Output data to cache + * @return void + */ + public function _write_cache($output) + { + $CI =& get_instance(); + $path = $CI->config->item('cache_path'); + $cache_path = ($path === '') ? APPPATH.'cache/' : $path; + + if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path)) + { + log_message('error', 'Unable to write cache file: '.$cache_path); + return; + } + + $uri = $CI->config->item('base_url') + .$CI->config->item('index_page') + .$CI->uri->uri_string(); + + if (($cache_query_string = $CI->config->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) + { + if (is_array($cache_query_string)) + { + $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); + } + else + { + $uri .= '?'.$_SERVER['QUERY_STRING']; + } + } + + $cache_path .= md5($uri); + + if ( ! $fp = @fopen($cache_path, 'w+b')) + { + log_message('error', 'Unable to write cache file: '.$cache_path); + return; + } + + if (flock($fp, LOCK_EX)) + { + // If output compression is enabled, compress the cache + // itself, so that we don't have to do that each time + // we're serving it + if ($this->_compress_output === TRUE) + { + $output = gzencode($output); + + if ($this->get_header('content-type') === NULL) + { + $this->set_content_type($this->mime_type); + } + } + + $expire = time() + ($this->cache_expiration * 60); + + // Put together our serialized info. + $cache_info = serialize(array( + 'expire' => $expire, + 'headers' => $this->headers + )); + + $output = $cache_info.'ENDCI--->'.$output; + + for ($written = 0, $length = strlen($output); $written < $length; $written += $result) + { + if (($result = fwrite($fp, substr($output, $written))) === FALSE) + { + break; + } + } + + flock($fp, LOCK_UN); + } + else + { + log_message('error', 'Unable to secure a file lock for file at: '.$cache_path); + return; + } + + fclose($fp); + + if (is_int($result)) + { + chmod($cache_path, 0640); + log_message('debug', 'Cache file written: '.$cache_path); + + // Send HTTP cache-control headers to browser to match file cache settings. + $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire); + } + else + { + @unlink($cache_path); + log_message('error', 'Unable to write the complete cache content at: '.$cache_path); + } + } + + // -------------------------------------------------------------------- + + /** + * Update/serve cached output + * + * @uses CI_Config + * @uses CI_URI + * + * @param object &$CFG CI_Config class instance + * @param object &$URI CI_URI class instance + * @return bool TRUE on success or FALSE on failure + */ + public function _display_cache(&$CFG, &$URI) + { + $cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache/' : $CFG->item('cache_path'); + + // Build the file path. The file name is an MD5 hash of the full URI + $uri = $CFG->item('base_url').$CFG->item('index_page').$URI->uri_string; + + if (($cache_query_string = $CFG->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) + { + if (is_array($cache_query_string)) + { + $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); + } + else + { + $uri .= '?'.$_SERVER['QUERY_STRING']; + } + } + + $filepath = $cache_path.md5($uri); + + if ( ! file_exists($filepath) OR ! $fp = @fopen($filepath, 'rb')) + { + return FALSE; + } + + flock($fp, LOCK_SH); + + $cache = (filesize($filepath) > 0) ? fread($fp, filesize($filepath)) : ''; + + flock($fp, LOCK_UN); + fclose($fp); + + // Look for embedded serialized file info. + if ( ! preg_match('/^(.*)ENDCI--->/', $cache, $match)) + { + return FALSE; + } + + $cache_info = unserialize($match[1]); + $expire = $cache_info['expire']; + + $last_modified = filemtime($filepath); + + // Has the file expired? + if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path)) + { + // If so we'll delete it. + @unlink($filepath); + log_message('debug', 'Cache file has expired. File deleted.'); + return FALSE; + } + else + { + // Or else send the HTTP cache control headers. + $this->set_cache_header($last_modified, $expire); + } + + // Add headers from cache file. + foreach ($cache_info['headers'] as $header) + { + $this->set_header($header[0], $header[1]); + } + + // Display the cache + $this->_display(substr($cache, strlen($match[0]))); + log_message('debug', 'Cache file is current. Sending it to browser.'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Delete cache + * + * @param string $uri URI string + * @return bool + */ + public function delete_cache($uri = '') + { + $CI =& get_instance(); + $cache_path = $CI->config->item('cache_path'); + if ($cache_path === '') + { + $cache_path = APPPATH.'cache/'; + } + + if ( ! is_dir($cache_path)) + { + log_message('error', 'Unable to find cache path: '.$cache_path); + return FALSE; + } + + if (empty($uri)) + { + $uri = $CI->uri->uri_string(); + + if (($cache_query_string = $CI->config->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) + { + if (is_array($cache_query_string)) + { + $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); + } + else + { + $uri .= '?'.$_SERVER['QUERY_STRING']; + } + } + } + + $cache_path .= md5($CI->config->item('base_url').$CI->config->item('index_page').ltrim($uri, '/')); + + if ( ! @unlink($cache_path)) + { + log_message('error', 'Unable to delete cache file for '.$uri); + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set Cache Header + * + * Set the HTTP headers to match the server-side file cache settings + * in order to reduce bandwidth. + * + * @param int $last_modified Timestamp of when the page was last modified + * @param int $expiration Timestamp of when should the requested page expire from cache + * @return void + */ + public function set_cache_header($last_modified, $expiration) + { + $max_age = $expiration - $_SERVER['REQUEST_TIME']; + + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) + { + $this->set_status_header(304); + exit; + } + else + { + header('Pragma: public'); + header('Cache-Control: max-age='.$max_age.', public'); + header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT'); + header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT'); + } + } + +} diff --git a/ci-3.0/system/core/Router.php b/ci-3.0/system/core/Router.php new file mode 100755 index 000000000..a84be1f1d --- /dev/null +++ b/ci-3.0/system/core/Router.php @@ -0,0 +1,514 @@ +config =& load_class('Config', 'core'); + $this->uri =& load_class('URI', 'core'); + + $this->enable_query_strings = ( ! is_cli() && $this->config->item('enable_query_strings') === TRUE); + + // If a directory override is configured, it has to be set before any dynamic routing logic + is_array($routing) && isset($routing['directory']) && $this->set_directory($routing['directory']); + $this->_set_routing(); + + // Set any routing overrides that may exist in the main index file + if (is_array($routing)) + { + empty($routing['controller']) OR $this->set_class($routing['controller']); + empty($routing['function']) OR $this->set_method($routing['function']); + } + + log_message('info', 'Router Class Initialized'); + } + + // -------------------------------------------------------------------- + + /** + * Set route mapping + * + * Determines what should be served based on the URI request, + * as well as any "routes" that have been set in the routing config file. + * + * @return void + */ + protected function _set_routing() + { + // Load the routes.php file. It would be great if we could + // skip this for enable_query_strings = TRUE, but then + // default_controller would be empty ... + if (file_exists(APPPATH.'config/routes.php')) + { + include(APPPATH.'config/routes.php'); + } + + if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/routes.php')) + { + include(APPPATH.'config/'.ENVIRONMENT.'/routes.php'); + } + + // Validate & get reserved routes + if (isset($route) && is_array($route)) + { + isset($route['default_controller']) && $this->default_controller = $route['default_controller']; + isset($route['translate_uri_dashes']) && $this->translate_uri_dashes = $route['translate_uri_dashes']; + unset($route['default_controller'], $route['translate_uri_dashes']); + $this->routes = $route; + } + + // Are query strings enabled in the config file? Normally CI doesn't utilize query strings + // since URI segments are more search-engine friendly, but they can optionally be used. + // If this feature is enabled, we will gather the directory/class/method a little differently + if ($this->enable_query_strings) + { + // If the directory is set at this time, it means an override exists, so skip the checks + if ( ! isset($this->directory)) + { + $_d = $this->config->item('directory_trigger'); + $_d = isset($_GET[$_d]) ? trim($_GET[$_d], " \t\n\r\0\x0B/") : ''; + + if ($_d !== '') + { + $this->uri->filter_uri($_d); + $this->set_directory($_d); + } + } + + $_c = trim($this->config->item('controller_trigger')); + if ( ! empty($_GET[$_c])) + { + $this->uri->filter_uri($_GET[$_c]); + $this->set_class($_GET[$_c]); + + $_f = trim($this->config->item('function_trigger')); + if ( ! empty($_GET[$_f])) + { + $this->uri->filter_uri($_GET[$_f]); + $this->set_method($_GET[$_f]); + } + + $this->uri->rsegments = array( + 1 => $this->class, + 2 => $this->method + ); + } + else + { + $this->_set_default_controller(); + } + + // Routing rules don't apply to query strings and we don't need to detect + // directories, so we're done here + return; + } + + // Is there anything to parse? + if ($this->uri->uri_string !== '') + { + $this->_parse_routes(); + } + else + { + $this->_set_default_controller(); + } + } + + // -------------------------------------------------------------------- + + /** + * Set request route + * + * Takes an array of URI segments as input and sets the class/method + * to be called. + * + * @used-by CI_Router::_parse_routes() + * @param array $segments URI segments + * @return void + */ + protected function _set_request($segments = array()) + { + $segments = $this->_validate_request($segments); + // If we don't have any segments left - try the default controller; + // WARNING: Directories get shifted out of the segments array! + if (empty($segments)) + { + $this->_set_default_controller(); + return; + } + + if ($this->translate_uri_dashes === TRUE) + { + $segments[0] = str_replace('-', '_', $segments[0]); + if (isset($segments[1])) + { + $segments[1] = str_replace('-', '_', $segments[1]); + } + } + + $this->set_class($segments[0]); + if (isset($segments[1])) + { + $this->set_method($segments[1]); + } + else + { + $segments[1] = 'index'; + } + + array_unshift($segments, NULL); + unset($segments[0]); + $this->uri->rsegments = $segments; + } + + // -------------------------------------------------------------------- + + /** + * Set default controller + * + * @return void + */ + protected function _set_default_controller() + { + if (empty($this->default_controller)) + { + show_error('Unable to determine what should be displayed. A default route has not been specified in the routing file.'); + } + + // Is the method being specified? + if (sscanf($this->default_controller, '%[^/]/%s', $class, $method) !== 2) + { + $method = 'index'; + } + + if ( ! file_exists(APPPATH.'controllers/'.$this->directory.ucfirst($class).'.php')) + { + // This will trigger 404 later + return; + } + + $this->set_class($class); + $this->set_method($method); + + // Assign routed segments, index starting from 1 + $this->uri->rsegments = array( + 1 => $class, + 2 => $method + ); + + log_message('debug', 'No URI present. Default controller set.'); + } + + // -------------------------------------------------------------------- + + /** + * Validate request + * + * Attempts validate the URI request and determine the controller path. + * + * @used-by CI_Router::_set_request() + * @param array $segments URI segments + * @return mixed URI segments + */ + protected function _validate_request($segments) + { + $c = count($segments); + $directory_override = isset($this->directory); + + // Loop through our segments and return as soon as a controller + // is found or when such a directory doesn't exist + while ($c-- > 0) + { + $test = $this->directory + .ucfirst($this->translate_uri_dashes === TRUE ? str_replace('-', '_', $segments[0]) : $segments[0]); + + if ( ! file_exists(APPPATH.'controllers/'.$test.'.php') + && $directory_override === FALSE + && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]) + ) + { + $this->set_directory(array_shift($segments), TRUE); + continue; + } + + return $segments; + } + + // This means that all segments were actually directories + return $segments; + } + + // -------------------------------------------------------------------- + + /** + * Parse Routes + * + * Matches any routes that may exist in the config/routes.php file + * against the URI to determine if the class/method need to be remapped. + * + * @return void + */ + protected function _parse_routes() + { + // Turn the segment array into a URI string + $uri = implode('/', $this->uri->segments); + + // Get HTTP verb + $http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli'; + + // Loop through the route array looking for wildcards + foreach ($this->routes as $key => $val) + { + // Check if route format is using HTTP verbs + if (is_array($val)) + { + $val = array_change_key_case($val, CASE_LOWER); + if (isset($val[$http_verb])) + { + $val = $val[$http_verb]; + } + else + { + continue; + } + } + + // Convert wildcards to RegEx + $key = str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $key); + + // Does the RegEx match? + if (preg_match('#^'.$key.'$#', $uri, $matches)) + { + // Are we using callbacks to process back-references? + if ( ! is_string($val) && is_callable($val)) + { + // Remove the original string from the matches array. + array_shift($matches); + + // Execute the callback using the values in matches as its parameters. + $val = call_user_func_array($val, $matches); + } + // Are we using the default routing method for back-references? + elseif (strpos($val, '$') !== FALSE && strpos($key, '(') !== FALSE) + { + $val = preg_replace('#^'.$key.'$#', $val, $uri); + } + + $this->_set_request(explode('/', $val)); + return; + } + } + + // If we got this far it means we didn't encounter a + // matching route so we'll set the site default route + $this->_set_request(array_values($this->uri->segments)); + } + + // -------------------------------------------------------------------- + + /** + * Set class name + * + * @param string $class Class name + * @return void + */ + public function set_class($class) + { + $this->class = str_replace(array('/', '.'), '', $class); + } + + // -------------------------------------------------------------------- + + /** + * Fetch the current class + * + * @deprecated 3.0.0 Read the 'class' property instead + * @return string + */ + public function fetch_class() + { + return $this->class; + } + + // -------------------------------------------------------------------- + + /** + * Set method name + * + * @param string $method Method name + * @return void + */ + public function set_method($method) + { + $this->method = $method; + } + + // -------------------------------------------------------------------- + + /** + * Fetch the current method + * + * @deprecated 3.0.0 Read the 'method' property instead + * @return string + */ + public function fetch_method() + { + return $this->method; + } + + // -------------------------------------------------------------------- + + /** + * Set directory name + * + * @param string $dir Directory name + * @param bool $append Whether we're appending rather than setting the full value + * @return void + */ + public function set_directory($dir, $append = FALSE) + { + if ($append !== TRUE OR empty($this->directory)) + { + $this->directory = str_replace('.', '', trim($dir, '/')).'/'; + } + else + { + $this->directory .= str_replace('.', '', trim($dir, '/')).'/'; + } + } + + // -------------------------------------------------------------------- + + /** + * Fetch directory + * + * Feches the sub-directory (if any) that contains the requested + * controller class. + * + * @deprecated 3.0.0 Read the 'directory' property instead + * @return string + */ + public function fetch_directory() + { + return $this->directory; + } + +} diff --git a/ci-3.0/system/core/Security.php b/ci-3.0/system/core/Security.php new file mode 100755 index 000000000..36dea4cf2 --- /dev/null +++ b/ci-3.0/system/core/Security.php @@ -0,0 +1,1030 @@ +', '<', '>', + "'", '"', '&', '$', '#', + '{', '}', '[', ']', '=', + ';', '?', '%20', '%22', + '%3c', // < + '%253c', // < + '%3e', // > + '%0e', // > + '%28', // ( + '%29', // ) + '%2528', // ( + '%26', // & + '%24', // $ + '%3f', // ? + '%3b', // ; + '%3d' // = + ); + + /** + * Character set + * + * Will be overridden by the constructor. + * + * @var string + */ + public $charset = 'UTF-8'; + + /** + * XSS Hash + * + * Random Hash for protecting URLs. + * + * @var string + */ + protected $_xss_hash; + + /** + * CSRF Hash + * + * Random hash for Cross Site Request Forgery protection cookie + * + * @var string + */ + protected $_csrf_hash; + + /** + * CSRF Expire time + * + * Expiration time for Cross Site Request Forgery protection cookie. + * Defaults to two hours (in seconds). + * + * @var int + */ + protected $_csrf_expire = 7200; + + /** + * CSRF Token name + * + * Token name for Cross Site Request Forgery protection cookie. + * + * @var string + */ + protected $_csrf_token_name = 'ci_csrf_token'; + + /** + * CSRF Cookie name + * + * Cookie name for Cross Site Request Forgery protection cookie. + * + * @var string + */ + protected $_csrf_cookie_name = 'ci_csrf_token'; + + /** + * List of never allowed strings + * + * @var array + */ + protected $_never_allowed_str = array( + 'document.cookie' => '[removed]', + 'document.write' => '[removed]', + '.parentNode' => '[removed]', + '.innerHTML' => '[removed]', + '-moz-binding' => '[removed]', + '' => '-->', + ' '<![CDATA[', + '' => '<comment>' + ); + + /** + * List of never allowed regex replacements + * + * @var array + */ + protected $_never_allowed_regex = array( + 'javascript\s*:', + '(document|(document\.)?window)\.(location|on\w*)', + 'expression\s*(\(|&\#40;)', // CSS and IE + 'vbscript\s*:', // IE, surprise! + 'wscript\s*:', // IE + 'jscript\s*:', // IE + 'vbs\s*:', // IE + 'Redirect\s+30\d', + "([\"'])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?" + ); + + /** + * Class constructor + * + * @return void + */ + public function __construct() + { + // Is CSRF protection enabled? + if (config_item('csrf_protection')) + { + // CSRF config + foreach (array('csrf_expire', 'csrf_token_name', 'csrf_cookie_name') as $key) + { + if (NULL !== ($val = config_item($key))) + { + $this->{'_'.$key} = $val; + } + } + + // Append application specific cookie prefix + if ($cookie_prefix = config_item('cookie_prefix')) + { + $this->_csrf_cookie_name = $cookie_prefix.$this->_csrf_cookie_name; + } + + // Set the CSRF hash + $this->_csrf_set_hash(); + } + + $this->charset = strtoupper(config_item('charset')); + + log_message('info', 'Security Class Initialized'); + } + + // -------------------------------------------------------------------- + + /** + * CSRF Verify + * + * @return CI_Security + */ + public function csrf_verify() + { + // If it's not a POST request we will set the CSRF cookie + if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') + { + return $this->csrf_set_cookie(); + } + + // Check if URI has been whitelisted from CSRF checks + if ($exclude_uris = config_item('csrf_exclude_uris')) + { + $uri = load_class('URI', 'core'); + foreach ($exclude_uris as $excluded) + { + if (preg_match('#^'.$excluded.'$#i'.(UTF8_ENABLED ? 'u' : ''), $uri->uri_string())) + { + return $this; + } + } + } + + // Do the tokens exist in both the _POST and _COOKIE arrays? + if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name]) + OR $_POST[$this->_csrf_token_name] !== $_COOKIE[$this->_csrf_cookie_name]) // Do the tokens match? + { + $this->csrf_show_error(); + } + + // We kill this since we're done and we don't want to polute the _POST array + unset($_POST[$this->_csrf_token_name]); + + // Regenerate on every submission? + if (config_item('csrf_regenerate')) + { + // Nothing should last forever + unset($_COOKIE[$this->_csrf_cookie_name]); + $this->_csrf_hash = NULL; + } + + $this->_csrf_set_hash(); + $this->csrf_set_cookie(); + + log_message('info', 'CSRF token verified'); + return $this; + } + + // -------------------------------------------------------------------- + + /** + * CSRF Set Cookie + * + * @codeCoverageIgnore + * @return CI_Security + */ + public function csrf_set_cookie() + { + $expire = time() + $this->_csrf_expire; + $secure_cookie = (bool) config_item('cookie_secure'); + + if ($secure_cookie && ! is_https()) + { + return FALSE; + } + + setcookie( + $this->_csrf_cookie_name, + $this->_csrf_hash, + $expire, + config_item('cookie_path'), + config_item('cookie_domain'), + $secure_cookie, + config_item('cookie_httponly') + ); + log_message('info', 'CSRF cookie sent'); + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Show CSRF Error + * + * @return void + */ + public function csrf_show_error() + { + show_error('The action you have requested is not allowed.', 403); + } + + // -------------------------------------------------------------------- + + /** + * Get CSRF Hash + * + * @see CI_Security::$_csrf_hash + * @return string CSRF hash + */ + public function get_csrf_hash() + { + return $this->_csrf_hash; + } + + // -------------------------------------------------------------------- + + /** + * Get CSRF Token Name + * + * @see CI_Security::$_csrf_token_name + * @return string CSRF token name + */ + public function get_csrf_token_name() + { + return $this->_csrf_token_name; + } + + // -------------------------------------------------------------------- + + /** + * XSS Clean + * + * Sanitizes data so that Cross Site Scripting Hacks can be + * prevented. This method does a fair amount of work but + * it is extremely thorough, designed to prevent even the + * most obscure XSS attempts. Nothing is ever 100% foolproof, + * of course, but I haven't been able to get anything passed + * the filter. + * + * Note: Should only be used to deal with data upon submission. + * It's not something that should be used for general + * runtime processing. + * + * @link http://channel.bitflux.ch/wiki/XSS_Prevention + * Based in part on some code and ideas from Bitflux. + * + * @link http://ha.ckers.org/xss.html + * To help develop this script I used this great list of + * vulnerabilities along with a few other hacks I've + * harvested from examining vulnerabilities in other programs. + * + * @param string|string[] $str Input data + * @param bool $is_image Whether the input is an image + * @return string + */ + public function xss_clean($str, $is_image = FALSE) + { + // Is the string an array? + if (is_array($str)) + { + while (list($key) = each($str)) + { + $str[$key] = $this->xss_clean($str[$key]); + } + + return $str; + } + + // Remove Invisible Characters + $str = remove_invisible_characters($str); + + /* + * URL Decode + * + * Just in case stuff like this is submitted: + * + * Google + * + * Note: Use rawurldecode() so it does not remove plus signs + */ + do + { + $str = rawurldecode($str); + } + while (preg_match('/%[0-9a-f]{2,}/i', $str)); + + /* + * Convert character entities to ASCII + * + * This permits our tests below to work reliably. + * We only convert entities that are within tags since + * these are the ones that will pose security problems. + */ + $str = preg_replace_callback("/[^a-z0-9>]+[a-z0-9]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str); + $str = preg_replace_callback('/<\w+.*/si', array($this, '_decode_entity'), $str); + + // Remove Invisible Characters Again! + $str = remove_invisible_characters($str); + + /* + * Convert all tabs to spaces + * + * This prevents strings like this: ja vascript + * NOTE: we deal with spaces between characters later. + * NOTE: preg_replace was found to be amazingly slow here on + * large blocks of data, so we use str_replace. + */ + $str = str_replace("\t", ' ', $str); + + // Capture converted string for later comparison + $converted_string = $str; + + // Remove Strings that are never allowed + $str = $this->_do_never_allowed($str); + + /* + * Makes PHP tags safe + * + * Note: XML tags are inadvertently replaced too: + * + * '), array('<?', '?>'), $str); + } + + /* + * Compact any exploded words + * + * This corrects words like: j a v a s c r i p t + * These words are compacted back to their correct state. + */ + $words = array( + 'javascript', 'expression', 'vbscript', 'jscript', 'wscript', + 'vbs', 'script', 'base64', 'applet', 'alert', 'document', + 'write', 'cookie', 'window', 'confirm', 'prompt', 'eval' + ); + + foreach ($words as $word) + { + $word = implode('\s*', str_split($word)).'\s*'; + + // We only want to do this when it is followed by a non-word character + // That way valid stuff like "dealer to" does not become "dealerto" + $str = preg_replace_callback('#('.substr($word, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str); + } + + /* + * Remove disallowed Javascript in links or img tags + * We used to do some version comparisons and use of stripos(), + * but it is dog slow compared to these simplified non-capturing + * preg_match(), especially if the pattern exists in the string + * + * Note: It was reported that not only space characters, but all in + * the following pattern can be parsed as separators between a tag name + * and its attributes: [\d\s"\'`;,\/\=\(\x00\x0B\x09\x0C] + * ... however, remove_invisible_characters() above already strips the + * hex-encoded ones, so we'll skip them below. + */ + do + { + $original = $str; + + if (preg_match('/]+([^>]*?)(?:>|$)#si', array($this, '_js_link_removal'), $str); + } + + if (preg_match('/]*?)(?:\s?/?>|$)#si', array($this, '_js_img_removal'), $str); + } + + if (preg_match('/script|xss/i', $str)) + { + $str = preg_replace('##si', '[removed]', $str); + } + } + while ($original !== $str); + unset($original); + + /* + * Sanitize naughty HTML elements + * + * If a tag containing any of the words in the list + * below is found, the tag gets converted to entities. + * + * So this: + * Becomes: <blink> + */ + $pattern = '#' + .'<((?/*\s*)(?[a-z0-9]+)(?=[^a-z0-9]|$)' // tag start and name, followed by a non-tag character + .'[^\s\042\047a-z0-9>/=]*' // a valid attribute character immediately after the tag would count as a separator + // optional attributes + .'(?(?:[\s\042\047/=]*' // non-attribute characters, excluding > (tag close) for obvious reasons + .'[^\s\042\047>/=]+' // attribute characters + // optional attribute-value + .'(?:\s*=' // attribute-value separator + .'(?:[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*))' // single, double or non-quoted value + .')?' // end optional attribute-value group + .')*)' // end optional attributes group + .'[^>]*)(?\>)?#isS'; + + // Note: It would be nice to optimize this for speed, BUT + // only matching the naughty elements here results in + // false positives and in turn - vulnerabilities! + do + { + $old_str = $str; + $str = preg_replace_callback($pattern, array($this, '_sanitize_naughty_html'), $str); + } + while ($old_str !== $str); + unset($old_str); + + /* + * Sanitize naughty scripting elements + * + * Similar to above, only instead of looking for + * tags it looks for PHP and JavaScript commands + * that are disallowed. Rather than removing the + * code, it simply converts the parenthesis to entities + * rendering the code un-executable. + * + * For example: eval('some code') + * Becomes: eval('some code') + */ + $str = preg_replace( + '#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', + '\\1\\2(\\3)', + $str + ); + + // Final clean up + // This adds a bit of extra precaution in case + // something got through the above filters + $str = $this->_do_never_allowed($str); + + /* + * Images are Handled in a Special Way + * - Essentially, we want to know that after all of the character + * conversion is done whether any unwanted, likely XSS, code was found. + * If not, we return TRUE, as the image is clean. + * However, if the string post-conversion does not matched the + * string post-removal of XSS, then it fails, as there was unwanted XSS + * code found and removed/changed during processing. + */ + if ($is_image === TRUE) + { + return ($str === $converted_string); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * XSS Hash + * + * Generates the XSS hash if needed and returns it. + * + * @see CI_Security::$_xss_hash + * @return string XSS hash + */ + public function xss_hash() + { + if ($this->_xss_hash === NULL) + { + $rand = $this->get_random_bytes(16); + $this->_xss_hash = ($rand === FALSE) + ? md5(uniqid(mt_rand(), TRUE)) + : bin2hex($rand); + } + + return $this->_xss_hash; + } + + // -------------------------------------------------------------------- + + /** + * Get random bytes + * + * @param int $length Output length + * @return string + */ + public function get_random_bytes($length) + { + if (empty($length) OR ! ctype_digit((string) $length)) + { + return FALSE; + } + + // Unfortunately, none of the following PRNGs is guaranteed to exist ... + if (defined('MCRYPT_DEV_URANDOM') && ($output = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)) !== FALSE) + { + return $output; + } + + + if (is_readable('/dev/urandom') && ($fp = fopen('/dev/urandom', 'rb')) !== FALSE) + { + // Try not to waste entropy ... + is_php('5.4') && stream_set_chunk_size($fp, $length); + $output = fread($fp, $length); + fclose($fp); + if ($output !== FALSE) + { + return $output; + } + } + + if (function_exists('openssl_random_pseudo_bytes')) + { + return openssl_random_pseudo_bytes($length); + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * HTML Entities Decode + * + * A replacement for html_entity_decode() + * + * The reason we are not using html_entity_decode() by itself is because + * while it is not technically correct to leave out the semicolon + * at the end of an entity most browsers will still interpret the entity + * correctly. html_entity_decode() does not convert entities without + * semicolons, so we are left with our own little solution here. Bummer. + * + * @link http://php.net/html-entity-decode + * + * @param string $str Input + * @param string $charset Character set + * @return string + */ + public function entity_decode($str, $charset = NULL) + { + if (strpos($str, '&') === FALSE) + { + return $str; + } + + static $_entities; + + isset($charset) OR $charset = $this->charset; + $flag = is_php('5.4') + ? ENT_COMPAT | ENT_HTML5 + : ENT_COMPAT; + + do + { + $str_compare = $str; + + // Decode standard entities, avoiding false positives + if (preg_match_all('/&[a-z]{2,}(?![a-z;])/i', $str, $matches)) + { + if ( ! isset($_entities)) + { + $_entities = array_map( + 'strtolower', + is_php('5.3.4') + ? get_html_translation_table(HTML_ENTITIES, $flag, $charset) + : get_html_translation_table(HTML_ENTITIES, $flag) + ); + + // If we're not on PHP 5.4+, add the possibly dangerous HTML 5 + // entities to the array manually + if ($flag === ENT_COMPAT) + { + $_entities[':'] = ':'; + $_entities['('] = '('; + $_entities[')'] = ')'; + $_entities["\n"] = '&newline;'; + $_entities["\t"] = '&tab;'; + } + } + + $replace = array(); + $matches = array_unique(array_map('strtolower', $matches[0])); + foreach ($matches as &$match) + { + if (($char = array_search($match.';', $_entities, TRUE)) !== FALSE) + { + $replace[$match] = $char; + } + } + + $str = str_ireplace(array_keys($replace), array_values($replace), $str); + } + + // Decode numeric & UTF16 two byte entities + $str = html_entity_decode( + preg_replace('/(&#(?:x0*[0-9a-f]{2,5}(?![0-9a-f;])|(?:0*\d{2,4}(?![0-9;]))))/iS', '$1;', $str), + $flag, + $charset + ); + } + while ($str_compare !== $str); + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Sanitize Filename + * + * @param string $str Input file name + * @param bool $relative_path Whether to preserve paths + * @return string + */ + public function sanitize_filename($str, $relative_path = FALSE) + { + $bad = $this->filename_bad_chars; + + if ( ! $relative_path) + { + $bad[] = './'; + $bad[] = '/'; + } + + $str = remove_invisible_characters($str, FALSE); + + do + { + $old = $str; + $str = str_replace($bad, '', $str); + } + while ($old !== $str); + + return stripslashes($str); + } + + // ---------------------------------------------------------------- + + /** + * Strip Image Tags + * + * @param string $str + * @return string + */ + public function strip_image_tags($str) + { + return preg_replace(array('##', '##'), '\\1', $str); + } + + // ---------------------------------------------------------------- + + /** + * Compact Exploded Words + * + * Callback method for xss_clean() to remove whitespace from + * things like 'j a v a s c r i p t'. + * + * @used-by CI_Security::xss_clean() + * @param array $matches + * @return string + */ + protected function _compact_exploded_words($matches) + { + return preg_replace('/\s+/s', '', $matches[1]).$matches[2]; + } + + // -------------------------------------------------------------------- + + /** + * Sanitize Naughty HTML + * + * Callback method for xss_clean() to remove naughty HTML elements. + * + * @used-by CI_Security::xss_clean() + * @param array $matches + * @return string + */ + protected function _sanitize_naughty_html($matches) + { + static $naughty_tags = array( + 'alert', 'prompt', 'confirm', 'applet', 'audio', 'basefont', 'base', 'behavior', 'bgsound', + 'blink', 'body', 'embed', 'expression', 'form', 'frameset', 'frame', 'head', 'html', 'ilayer', + 'iframe', 'input', 'button', 'select', 'isindex', 'layer', 'link', 'meta', 'keygen', 'object', + 'plaintext', 'style', 'script', 'textarea', 'title', 'math', 'video', 'svg', 'xml', 'xss' + ); + + static $evil_attributes = array( + 'on\w+', 'style', 'xmlns', 'formaction', 'form', 'xlink:href', 'FSCommand', 'seekSegmentTime' + ); + + // First, escape unclosed tags + if (empty($matches['closeTag'])) + { + return '<'.$matches[1]; + } + // Is the element that we caught naughty? If so, escape it + elseif (in_array(strtolower($matches['tagName']), $naughty_tags, TRUE)) + { + return '<'.$matches[1].'>'; + } + // For other tags, see if their attributes are "evil" and strip those + elseif (isset($matches['attributes'])) + { + // We'll store the already fitlered attributes here + $attributes = array(); + + // Attribute-catching pattern + $attributes_pattern = '#' + .'(?[^\s\042\047>/=]+)' // attribute characters + // optional attribute-value + .'(?:\s*=(?[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*)))' // attribute-value separator + .'#i'; + + // Blacklist pattern for evil attribute names + $is_evil_pattern = '#^('.implode('|', $evil_attributes).')$#i'; + + // Each iteration filters a single attribute + do + { + // Strip any non-alpha characters that may preceed an attribute. + // Browsers often parse these incorrectly and that has been a + // of numerous XSS issues we've had. + $matches['attributes'] = preg_replace('#^[^a-z]+#i', '', $matches['attributes']); + + if ( ! preg_match($attributes_pattern, $matches['attributes'], $attribute, PREG_OFFSET_CAPTURE)) + { + // No (valid) attribute found? Discard everything else inside the tag + break; + } + + if ( + // Is it indeed an "evil" attribute? + preg_match($is_evil_pattern, $attribute['name'][0]) + // Or does it have an equals sign, but no value and not quoted? Strip that too! + OR (trim($attribute['value'][0]) === '') + ) + { + $attributes[] = 'xss=removed'; + } + else + { + $attributes[] = $attribute[0][0]; + } + + $matches['attributes'] = substr($matches['attributes'], $attribute[0][1] + strlen($attribute[0][0])); + } + while ($matches['attributes'] !== ''); + + $attributes = empty($attributes) + ? '' + : ' '.implode(' ', $attributes); + return '<'.$matches['slash'].$matches['tagName'].$attributes.'>'; + } + + return $matches[0]; + } + + // -------------------------------------------------------------------- + + /** + * JS Link Removal + * + * Callback method for xss_clean() to sanitize links. + * + * This limits the PCRE backtracks, making it more performance friendly + * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in + * PHP 5.2+ on link-heavy strings. + * + * @used-by CI_Security::xss_clean() + * @param array $match + * @return string + */ + protected function _js_link_removal($match) + { + return str_replace( + $match[1], + preg_replace( + '#href=.*?(?:(?:alert|prompt|confirm)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|_filter_attributes($match[1]) + ), + $match[0] + ); + } + + // -------------------------------------------------------------------- + + /** + * JS Image Removal + * + * Callback method for xss_clean() to sanitize image tags. + * + * This limits the PCRE backtracks, making it more performance friendly + * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in + * PHP 5.2+ on image tag heavy strings. + * + * @used-by CI_Security::xss_clean() + * @param array $match + * @return string + */ + protected function _js_img_removal($match) + { + return str_replace( + $match[1], + preg_replace( + '#src=.*?(?:(?:alert|prompt|confirm|eval)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|_filter_attributes($match[1]) + ), + $match[0] + ); + } + + // -------------------------------------------------------------------- + + /** + * Attribute Conversion + * + * @used-by CI_Security::xss_clean() + * @param array $match + * @return string + */ + protected function _convert_attribute($match) + { + return str_replace(array('>', '<', '\\'), array('>', '<', '\\\\'), $match[0]); + } + + // -------------------------------------------------------------------- + + /** + * Filter Attributes + * + * Filters tag attributes for consistency and safety. + * + * @used-by CI_Security::_js_img_removal() + * @used-by CI_Security::_js_link_removal() + * @param string $str + * @return string + */ + protected function _filter_attributes($str) + { + $out = ''; + if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches)) + { + foreach ($matches[0] as $match) + { + $out .= preg_replace('#/\*.*?\*/#s', '', $match); + } + } + + return $out; + } + + // -------------------------------------------------------------------- + + /** + * HTML Entity Decode Callback + * + * @used-by CI_Security::xss_clean() + * @param array $match + * @return string + */ + protected function _decode_entity($match) + { + // Protect GET variables in URLs + // 901119URL5918AMP18930PROTECT8198 + $match = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-/]+)|i', $this->xss_hash().'\\1=\\2', $match[0]); + + // Decode, then un-protect URL GET vars + return str_replace( + $this->xss_hash(), + '&', + $this->entity_decode($match, $this->charset) + ); + } + + // -------------------------------------------------------------------- + + /** + * Do Never Allowed + * + * @used-by CI_Security::xss_clean() + * @param string + * @return string + */ + protected function _do_never_allowed($str) + { + $str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str); + + foreach ($this->_never_allowed_regex as $regex) + { + $str = preg_replace('#'.$regex.'#is', '[removed]', $str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Set CSRF Hash and Cookie + * + * @return string + */ + protected function _csrf_set_hash() + { + if ($this->_csrf_hash === NULL) + { + // If the cookie exists we will use its value. + // We don't necessarily want to regenerate it with + // each page load since a page could contain embedded + // sub-pages causing this feature to fail + if (isset($_COOKIE[$this->_csrf_cookie_name]) && is_string($_COOKIE[$this->_csrf_cookie_name]) + && preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name]) === 1) + { + return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name]; + } + + $rand = $this->get_random_bytes(16); + $this->_csrf_hash = ($rand === FALSE) + ? md5(uniqid(mt_rand(), TRUE)) + : bin2hex($rand); + } + + return $this->_csrf_hash; + } + +} diff --git a/ci-3.0/system/core/URI.php b/ci-3.0/system/core/URI.php new file mode 100755 index 000000000..5b658f679 --- /dev/null +++ b/ci-3.0/system/core/URI.php @@ -0,0 +1,643 @@ +config =& load_class('Config', 'core'); + + // If query strings are enabled, we don't need to parse any segments. + // However, they don't make sense under CLI. + if (is_cli() OR $this->config->item('enable_query_strings') !== TRUE) + { + $this->_permitted_uri_chars = $this->config->item('permitted_uri_chars'); + + // If it's a CLI request, ignore the configuration + if (is_cli()) + { + $uri = $this->_parse_argv(); + } + else + { + $protocol = $this->config->item('uri_protocol'); + empty($protocol) && $protocol = 'REQUEST_URI'; + + switch ($protocol) + { + case 'AUTO': // For BC purposes only + case 'REQUEST_URI': + $uri = $this->_parse_request_uri(); + break; + case 'QUERY_STRING': + $uri = $this->_parse_query_string(); + break; + case 'PATH_INFO': + default: + $uri = isset($_SERVER[$protocol]) + ? $_SERVER[$protocol] + : $this->_parse_request_uri(); + break; + } + } + + $this->_set_uri_string($uri); + } + + log_message('info', 'URI Class Initialized'); + } + + // -------------------------------------------------------------------- + + /** + * Set URI String + * + * @param string $str + * @return void + */ + protected function _set_uri_string($str) + { + // Filter out control characters and trim slashes + $this->uri_string = trim(remove_invisible_characters($str, FALSE), '/'); + + if ($this->uri_string !== '') + { + // Remove the URL suffix, if present + if (($suffix = (string) $this->config->item('url_suffix')) !== '') + { + $slen = strlen($suffix); + + if (substr($this->uri_string, -$slen) === $suffix) + { + $this->uri_string = substr($this->uri_string, 0, -$slen); + } + } + + $this->segments[0] = NULL; + // Populate the segments array + foreach (explode('/', trim($this->uri_string, '/')) as $val) + { + $val = trim($val); + // Filter segments for security + $this->filter_uri($val); + + if ($val !== '') + { + $this->segments[] = $val; + } + } + + unset($this->segments[0]); + } + } + + // -------------------------------------------------------------------- + + /** + * Parse REQUEST_URI + * + * Will parse REQUEST_URI and automatically detect the URI from it, + * while fixing the query string if necessary. + * + * @return string + */ + protected function _parse_request_uri() + { + if ( ! isset($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME'])) + { + return ''; + } + + // parse_url() returns false if no host is present, but the path or query string + // contains a colon followed by a number + $uri = parse_url('http://dummy'.$_SERVER['REQUEST_URI']); + $query = isset($uri['query']) ? $uri['query'] : ''; + $uri = isset($uri['path']) ? $uri['path'] : ''; + + if (isset($_SERVER['SCRIPT_NAME'][0])) + { + if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) + { + $uri = (string) substr($uri, strlen($_SERVER['SCRIPT_NAME'])); + } + elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) + { + $uri = (string) substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME']))); + } + } + + // This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct + // URI is found, and also fixes the QUERY_STRING server var and $_GET array. + if (trim($uri, '/') === '' && strncmp($query, '/', 1) === 0) + { + $query = explode('?', $query, 2); + $uri = $query[0]; + $_SERVER['QUERY_STRING'] = isset($query[1]) ? $query[1] : ''; + } + else + { + $_SERVER['QUERY_STRING'] = $query; + } + + parse_str($_SERVER['QUERY_STRING'], $_GET); + + if ($uri === '/' OR $uri === '') + { + return '/'; + } + + // Do some final cleaning of the URI and return it + return $this->_remove_relative_directory($uri); + } + + // -------------------------------------------------------------------- + + /** + * Parse QUERY_STRING + * + * Will parse QUERY_STRING and automatically detect the URI from it. + * + * @return string + */ + protected function _parse_query_string() + { + $uri = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING'); + + if (trim($uri, '/') === '') + { + return ''; + } + elseif (strncmp($uri, '/', 1) === 0) + { + $uri = explode('?', $uri, 2); + $_SERVER['QUERY_STRING'] = isset($uri[1]) ? $uri[1] : ''; + $uri = $uri[0]; + } + + parse_str($_SERVER['QUERY_STRING'], $_GET); + + return $this->_remove_relative_directory($uri); + } + + // -------------------------------------------------------------------- + + /** + * Parse CLI arguments + * + * Take each command line argument and assume it is a URI segment. + * + * @return string + */ + protected function _parse_argv() + { + $args = array_slice($_SERVER['argv'], 1); + return $args ? implode('/', $args) : ''; + } + + // -------------------------------------------------------------------- + + /** + * Remove relative directory (../) and multi slashes (///) + * + * Do some final cleaning of the URI and return it, currently only used in self::_parse_request_uri() + * + * @param string $url + * @return string + */ + protected function _remove_relative_directory($uri) + { + $uris = array(); + $tok = strtok($uri, '/'); + while ($tok !== FALSE) + { + if (( ! empty($tok) OR $tok === '0') && $tok !== '..') + { + $uris[] = $tok; + } + $tok = strtok('/'); + } + + return implode('/', $uris); + } + + // -------------------------------------------------------------------- + + /** + * Filter URI + * + * Filters segments for malicious characters. + * + * @param string $str + * @return void + */ + public function filter_uri(&$str) + { + if ( ! empty($str) && ! empty($this->_permitted_uri_chars) && ! preg_match('/^['.$this->_permitted_uri_chars.']+$/i'.(UTF8_ENABLED ? 'u' : ''), $str)) + { + show_error('The URI you submitted has disallowed characters.', 400); + } + } + + // -------------------------------------------------------------------- + + /** + * Fetch URI Segment + * + * @see CI_URI::$segments + * @param int $n Index + * @param mixed $no_result What to return if the segment index is not found + * @return mixed + */ + public function segment($n, $no_result = NULL) + { + return isset($this->segments[$n]) ? $this->segments[$n] : $no_result; + } + + // -------------------------------------------------------------------- + + /** + * Fetch URI "routed" Segment + * + * Returns the re-routed URI segment (assuming routing rules are used) + * based on the index provided. If there is no routing, will return + * the same result as CI_URI::segment(). + * + * @see CI_URI::$rsegments + * @see CI_URI::segment() + * @param int $n Index + * @param mixed $no_result What to return if the segment index is not found + * @return mixed + */ + public function rsegment($n, $no_result = NULL) + { + return isset($this->rsegments[$n]) ? $this->rsegments[$n] : $no_result; + } + + // -------------------------------------------------------------------- + + /** + * URI to assoc + * + * Generates an associative array of URI data starting at the supplied + * segment index. For example, if this is your URI: + * + * example.com/user/search/name/joe/location/UK/gender/male + * + * You can use this method to generate an array with this prototype: + * + * array ( + * name => joe + * location => UK + * gender => male + * ) + * + * @param int $n Index (default: 3) + * @param array $default Default values + * @return array + */ + public function uri_to_assoc($n = 3, $default = array()) + { + return $this->_uri_to_assoc($n, $default, 'segment'); + } + + // -------------------------------------------------------------------- + + /** + * Routed URI to assoc + * + * Identical to CI_URI::uri_to_assoc(), only it uses the re-routed + * segment array. + * + * @see CI_URI::uri_to_assoc() + * @param int $n Index (default: 3) + * @param array $default Default values + * @return array + */ + public function ruri_to_assoc($n = 3, $default = array()) + { + return $this->_uri_to_assoc($n, $default, 'rsegment'); + } + + // -------------------------------------------------------------------- + + /** + * Internal URI-to-assoc + * + * Generates a key/value pair from the URI string or re-routed URI string. + * + * @used-by CI_URI::uri_to_assoc() + * @used-by CI_URI::ruri_to_assoc() + * @param int $n Index (default: 3) + * @param array $default Default values + * @param string $which Array name ('segment' or 'rsegment') + * @return array + */ + protected function _uri_to_assoc($n = 3, $default = array(), $which = 'segment') + { + if ( ! is_numeric($n)) + { + return $default; + } + + if (isset($this->keyval[$which], $this->keyval[$which][$n])) + { + return $this->keyval[$which][$n]; + } + + $total_segments = "total_{$which}s"; + $segment_array = "{$which}_array"; + + if ($this->$total_segments() < $n) + { + return (count($default) === 0) + ? array() + : array_fill_keys($default, NULL); + } + + $segments = array_slice($this->$segment_array(), ($n - 1)); + $i = 0; + $lastval = ''; + $retval = array(); + foreach ($segments as $seg) + { + if ($i % 2) + { + $retval[$lastval] = $seg; + } + else + { + $retval[$seg] = NULL; + $lastval = $seg; + } + + $i++; + } + + if (count($default) > 0) + { + foreach ($default as $val) + { + if ( ! array_key_exists($val, $retval)) + { + $retval[$val] = NULL; + } + } + } + + // Cache the array for reuse + isset($this->keyval[$which]) OR $this->keyval[$which] = array(); + $this->keyval[$which][$n] = $retval; + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Assoc to URI + * + * Generates a URI string from an associative array. + * + * @param array $array Input array of key/value pairs + * @return string URI string + */ + public function assoc_to_uri($array) + { + $temp = array(); + foreach ((array) $array as $key => $val) + { + $temp[] = $key; + $temp[] = $val; + } + + return implode('/', $temp); + } + + // -------------------------------------------------------------------- + + /** + * Slash segment + * + * Fetches an URI segment with a slash. + * + * @param int $n Index + * @param string $where Where to add the slash ('trailing' or 'leading') + * @return string + */ + public function slash_segment($n, $where = 'trailing') + { + return $this->_slash_segment($n, $where, 'segment'); + } + + // -------------------------------------------------------------------- + + /** + * Slash routed segment + * + * Fetches an URI routed segment with a slash. + * + * @param int $n Index + * @param string $where Where to add the slash ('trailing' or 'leading') + * @return string + */ + public function slash_rsegment($n, $where = 'trailing') + { + return $this->_slash_segment($n, $where, 'rsegment'); + } + + // -------------------------------------------------------------------- + + /** + * Internal Slash segment + * + * Fetches an URI Segment and adds a slash to it. + * + * @used-by CI_URI::slash_segment() + * @used-by CI_URI::slash_rsegment() + * + * @param int $n Index + * @param string $where Where to add the slash ('trailing' or 'leading') + * @param string $which Array name ('segment' or 'rsegment') + * @return string + */ + protected function _slash_segment($n, $where = 'trailing', $which = 'segment') + { + $leading = $trailing = '/'; + + if ($where === 'trailing') + { + $leading = ''; + } + elseif ($where === 'leading') + { + $trailing = ''; + } + + return $leading.$this->$which($n).$trailing; + } + + // -------------------------------------------------------------------- + + /** + * Segment Array + * + * @return array CI_URI::$segments + */ + public function segment_array() + { + return $this->segments; + } + + // -------------------------------------------------------------------- + + /** + * Routed Segment Array + * + * @return array CI_URI::$rsegments + */ + public function rsegment_array() + { + return $this->rsegments; + } + + // -------------------------------------------------------------------- + + /** + * Total number of segments + * + * @return int + */ + public function total_segments() + { + return count($this->segments); + } + + // -------------------------------------------------------------------- + + /** + * Total number of routed segments + * + * @return int + */ + public function total_rsegments() + { + return count($this->rsegments); + } + + // -------------------------------------------------------------------- + + /** + * Fetch URI string + * + * @return string CI_URI::$uri_string + */ + public function uri_string() + { + return $this->uri_string; + } + + // -------------------------------------------------------------------- + + /** + * Fetch Re-routed URI string + * + * @return string + */ + public function ruri_string() + { + return ltrim(load_class('Router', 'core')->directory, '/').implode('/', $this->rsegments); + } + +} diff --git a/codeigniter-3.0/system/core/Utf8.php b/ci-3.0/system/core/Utf8.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/core/Utf8.php rename to ci-3.0/system/core/Utf8.php diff --git a/ci-3.0/system/core/compat/hash.php b/ci-3.0/system/core/compat/hash.php new file mode 100755 index 000000000..15954559c --- /dev/null +++ b/ci-3.0/system/core/compat/hash.php @@ -0,0 +1,245 @@ + 32, + 'haval128,3' => 128, + 'haval160,3' => 128, + 'haval192,3' => 128, + 'haval224,3' => 128, + 'haval256,3' => 128, + 'haval128,4' => 128, + 'haval160,4' => 128, + 'haval192,4' => 128, + 'haval224,4' => 128, + 'haval256,4' => 128, + 'haval128,5' => 128, + 'haval160,5' => 128, + 'haval192,5' => 128, + 'haval224,5' => 128, + 'haval256,5' => 128, + 'md2' => 16, + 'md4' => 64, + 'md5' => 64, + 'ripemd128' => 64, + 'ripemd160' => 64, + 'ripemd256' => 64, + 'ripemd320' => 64, + 'salsa10' => 64, + 'salsa20' => 64, + 'sha1' => 64, + 'sha224' => 64, + 'sha256' => 64, + 'sha384' => 128, + 'sha512' => 128, + 'snefru' => 32, + 'snefru256' => 32, + 'tiger128,3' => 64, + 'tiger160,3' => 64, + 'tiger192,3' => 64, + 'tiger128,4' => 64, + 'tiger160,4' => 64, + 'tiger192,4' => 64, + 'whirlpool' => 64 + ); + + if (isset($block_sizes[$algo]) && strlen($password) > $block_sizes[$algo]) + { + $password = hash($algo, $password, TRUE); + } + + $hash = ''; + // Note: Blocks are NOT 0-indexed + for ($bc = ceil($length / $hash_length), $bi = 1; $bi <= $bc; $bi++) + { + $key = $derived_key = hash_hmac($algo, $salt.pack('N', $bi), $password, TRUE); + for ($i = 1; $i < $iterations; $i++) + { + $derived_key ^= $key = hash_hmac($algo, $key, $password, TRUE); + } + + $hash .= $derived_key; + } + + // This is not RFC-compatible, but we're aiming for natural PHP compatibility + return substr($raw_output ? $hash : bin2hex($hash), 0, $length); + } +} diff --git a/codeigniter-3.0/system/core/compat/index.html b/ci-3.0/system/core/compat/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/core/compat/index.html rename to ci-3.0/system/core/compat/index.html diff --git a/ci-3.0/system/core/compat/mbstring.php b/ci-3.0/system/core/compat/mbstring.php new file mode 100755 index 000000000..e335c85f7 --- /dev/null +++ b/ci-3.0/system/core/compat/mbstring.php @@ -0,0 +1,149 @@ + $val) + { + $this->$key = $val; + } + } + + log_message('info', 'Database Driver Class Initialized'); + } + + // -------------------------------------------------------------------- + + /** + * Initialize Database Settings + * + * @return bool + */ + public function initialize() + { + /* If an established connection is available, then there's + * no need to connect and select the database. + * + * Depending on the database driver, conn_id can be either + * boolean TRUE, a resource or an object. + */ + if ($this->conn_id) + { + return TRUE; + } + + // ---------------------------------------------------------------- + + // Connect to the database and set the connection ID + $this->conn_id = $this->db_connect($this->pconnect); + + // No connection resource? Check if there is a failover else throw an error + if ( ! $this->conn_id) + { + // Check if there is a failover set + if ( ! empty($this->failover) && is_array($this->failover)) + { + // Go over all the failovers + foreach ($this->failover as $failover) + { + // Replace the current settings with those of the failover + foreach ($failover as $key => $val) + { + $this->$key = $val; + } + + // Try to connect + $this->conn_id = $this->db_connect($this->pconnect); + + // If a connection is made break the foreach loop + if ($this->conn_id) + { + break; + } + } + } + + // We still don't have a connection? + if ( ! $this->conn_id) + { + log_message('error', 'Unable to connect to the database'); + + if ($this->db_debug) + { + $this->display_error('db_unable_to_connect'); + } + + return FALSE; + } + } + + // Now we set the character set and that's all + return $this->db_set_charset($this->char_set); + } + + // -------------------------------------------------------------------- + + /** + * DB connect + * + * This is just a dummy method that all drivers will override. + * + * @return mixed + */ + public function db_connect() + { + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @return mixed + */ + public function db_pconnect() + { + return $this->db_connect(TRUE); + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout. + * + * This is just a dummy method to allow drivers without such + * functionality to not declare it, while others will override it. + * + * @return void + */ + public function reconnect() + { + } + + // -------------------------------------------------------------------- + + /** + * Select database + * + * This is just a dummy method to allow drivers without such + * functionality to not declare it, while others will override it. + * + * @return bool + */ + public function db_select() + { + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @param string + * @return bool + */ + public function db_set_charset($charset) + { + if (method_exists($this, '_db_set_charset') && ! $this->_db_set_charset($charset)) + { + log_message('error', 'Unable to set database connection charset: '.$charset); + + if ($this->db_debug) + { + $this->display_error('db_unable_to_set_charset', $charset); + } + + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * The name of the platform in use (mysql, mssql, etc...) + * + * @return string + */ + public function platform() + { + return $this->dbdriver; + } + + // -------------------------------------------------------------------- + + /** + * Database version number + * + * Returns a string containing the version of the database being used. + * Most drivers will override this method. + * + * @return string + */ + public function version() + { + if (isset($this->data_cache['version'])) + { + return $this->data_cache['version']; + } + + if (FALSE === ($sql = $this->_version())) + { + return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE; + } + + $query = $this->query($sql)->row(); + return $this->data_cache['version'] = $query->ver; + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @return string + */ + protected function _version() + { + return 'SELECT VERSION() AS ver'; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * Accepts an SQL string as input and returns a result object upon + * successful execution of a "read" type query. Returns boolean TRUE + * upon successful execution of a "write" type query. Returns boolean + * FALSE upon failure, and if the $db_debug variable is set to TRUE + * will raise an error. + * + * @param string $sql + * @param array $binds = FALSE An array of binding data + * @param bool $return_object = NULL + * @return mixed + */ + public function query($sql, $binds = FALSE, $return_object = NULL) + { + if ($sql === '') + { + log_message('error', 'Invalid query: '.$sql); + return ($this->db_debug) ? $this->display_error('db_invalid_query') : FALSE; + } + elseif ( ! is_bool($return_object)) + { + $return_object = ! $this->is_write_type($sql); + } + + // Verify table prefix and replace if necessary + if ($this->dbprefix !== '' && $this->swap_pre !== '' && $this->dbprefix !== $this->swap_pre) + { + $sql = preg_replace('/(\W)'.$this->swap_pre.'(\S+?)/', '\\1'.$this->dbprefix.'\\2', $sql); + } + + // Compile binds if needed + if ($binds !== FALSE) + { + $sql = $this->compile_binds($sql, $binds); + } + + // Is query caching enabled? If the query is a "read type" + // we will load the caching class and return the previously + // cached query if it exists + if ($this->cache_on === TRUE && $return_object === TRUE && $this->_cache_init()) + { + $this->load_rdriver(); + if (FALSE !== ($cache = $this->CACHE->read($sql))) + { + return $cache; + } + } + + // Save the query for debugging + if ($this->save_queries === TRUE) + { + $this->queries[] = $sql; + } + + // Start the Query Timer + $time_start = microtime(TRUE); + + // Run the Query + if (FALSE === ($this->result_id = $this->simple_query($sql))) + { + if ($this->save_queries === TRUE) + { + $this->query_times[] = 0; + } + + // This will trigger a rollback if transactions are being used + if ($this->_trans_depth !== 0) + { + $this->_trans_status = FALSE; + } + + // Grab the error now, as we might run some additional queries before displaying the error + $error = $this->error(); + + // Log errors + log_message('error', 'Query error: '.$error['message'].' - Invalid query: '.$sql); + + if ($this->db_debug) + { + // We call this function in order to roll-back queries + // if transactions are enabled. If we don't call this here + // the error message will trigger an exit, causing the + // transactions to remain in limbo. + if ($this->_trans_depth !== 0) + { + do + { + $trans_depth = $this->_trans_depth; + $this->trans_complete(); + if ($trans_depth === $this->_trans_depth) + { + log_message('error', 'Database: Failure during an automated transaction commit/rollback!'); + break; + } + } + while ($this->_trans_depth !== 0); + } + + // Display errors + return $this->display_error(array('Error Number: '.$error['code'], $error['message'], $sql)); + } + + return FALSE; + } + + // Stop and aggregate the query time results + $time_end = microtime(TRUE); + $this->benchmark += $time_end - $time_start; + + if ($this->save_queries === TRUE) + { + $this->query_times[] = $time_end - $time_start; + } + + // Increment the query counter + $this->query_count++; + + // Will we have a result object instantiated? If not - we'll simply return TRUE + if ($return_object !== TRUE) + { + // If caching is enabled we'll auto-cleanup any existing files related to this particular URI + if ($this->cache_on === TRUE && $this->cache_autodel === TRUE && $this->_cache_init()) + { + $this->CACHE->delete(); + } + + return TRUE; + } + + // Load and instantiate the result driver + $driver = $this->load_rdriver(); + $RES = new $driver($this); + + // Is query caching enabled? If so, we'll serialize the + // result object and save it to a cache file. + if ($this->cache_on === TRUE && $this->_cache_init()) + { + // We'll create a new instance of the result object + // only without the platform specific driver since + // we can't use it with cached data (the query result + // resource ID won't be any good once we've cached the + // result object, so we'll have to compile the data + // and save it) + $CR = new CI_DB_result($this); + $CR->result_object = $RES->result_object(); + $CR->result_array = $RES->result_array(); + $CR->num_rows = $RES->num_rows(); + + // Reset these since cached objects can not utilize resource IDs. + $CR->conn_id = NULL; + $CR->result_id = NULL; + + $this->CACHE->write($sql, $CR); + } + + return $RES; + } + + // -------------------------------------------------------------------- + + /** + * Load the result drivers + * + * @return string the name of the result class + */ + public function load_rdriver() + { + $driver = 'CI_DB_'.$this->dbdriver.'_result'; + + if ( ! class_exists($driver, FALSE)) + { + require_once(BASEPATH.'database/DB_result.php'); + require_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result.php'); + } + + return $driver; + } + + // -------------------------------------------------------------------- + + /** + * Simple Query + * This is a simplified version of the query() function. Internally + * we only use it when running transaction commands since they do + * not require all the features of the main query() function. + * + * @param string the sql query + * @return mixed + */ + public function simple_query($sql) + { + if ( ! $this->conn_id) + { + $this->initialize(); + } + + return $this->_execute($sql); + } + + // -------------------------------------------------------------------- + + /** + * Disable Transactions + * This permits transactions to be disabled at run-time. + * + * @return void + */ + public function trans_off() + { + $this->trans_enabled = FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Enable/disable Transaction Strict Mode + * + * When strict mode is enabled, if you are running multiple groups of + * transactions, if one group fails all subsequent groups will be + * rolled back. + * + * If strict mode is disabled, each group is treated autonomously, + * meaning a failure of one group will not affect any others + * + * @param bool $mode = TRUE + * @return void + */ + public function trans_strict($mode = TRUE) + { + $this->trans_strict = is_bool($mode) ? $mode : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Start Transaction + * + * @param bool $test_mode = FALSE + * @return bool + */ + public function trans_start($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return FALSE; + } + + return $this->trans_begin($test_mode); + } + + // -------------------------------------------------------------------- + + /** + * Complete Transaction + * + * @return bool + */ + public function trans_complete() + { + if ( ! $this->trans_enabled) + { + return FALSE; + } + + // The query() function will set this flag to FALSE in the event that a query failed + if ($this->_trans_status === FALSE OR $this->_trans_failure === TRUE) + { + $this->trans_rollback(); + + // If we are NOT running in strict mode, we will reset + // the _trans_status flag so that subsequent groups of + // transactions will be permitted. + if ($this->trans_strict === FALSE) + { + $this->_trans_status = TRUE; + } + + log_message('debug', 'DB Transaction Failure'); + return FALSE; + } + + return $this->trans_commit(); + } + + // -------------------------------------------------------------------- + + /** + * Lets you retrieve the transaction flag to determine if it has failed + * + * @return bool + */ + public function trans_status() + { + return $this->_trans_status; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @param bool $test_mode + * @return bool + */ + public function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return FALSE; + } + // When transactions are nested we only begin/commit/rollback the outermost ones + elseif ($this->_trans_depth > 0) + { + $this->_trans_depth++; + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE); + + if ($this->_trans_begin()) + { + $this->_trans_depth++; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + public function trans_commit() + { + if ( ! $this->trans_enabled OR $this->_trans_depth === 0) + { + return FALSE; + } + // When transactions are nested we only begin/commit/rollback the outermost ones + elseif ($this->_trans_depth > 1 OR $this->_trans_commit()) + { + $this->_trans_depth--; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + public function trans_rollback() + { + if ( ! $this->trans_enabled OR $this->_trans_depth === 0) + { + return FALSE; + } + // When transactions are nested we only begin/commit/rollback the outermost ones + elseif ($this->_trans_depth > 1 OR $this->_trans_rollback()) + { + $this->_trans_depth--; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Compile Bindings + * + * @param string the sql statement + * @param array an array of bind data + * @return string + */ + public function compile_binds($sql, $binds) + { + if (empty($binds) OR empty($this->bind_marker) OR strpos($sql, $this->bind_marker) === FALSE) + { + return $sql; + } + elseif ( ! is_array($binds)) + { + $binds = array($binds); + $bind_count = 1; + } + else + { + // Make sure we're using numeric keys + $binds = array_values($binds); + $bind_count = count($binds); + } + + // We'll need the marker length later + $ml = strlen($this->bind_marker); + + // Make sure not to replace a chunk inside a string that happens to match the bind marker + if ($c = preg_match_all("/'[^']*'/i", $sql, $matches)) + { + $c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', + str_replace($matches[0], + str_replace($this->bind_marker, str_repeat(' ', $ml), $matches[0]), + $sql, $c), + $matches, PREG_OFFSET_CAPTURE); + + // Bind values' count must match the count of markers in the query + if ($bind_count !== $c) + { + return $sql; + } + } + elseif (($c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bind_count) + { + return $sql; + } + + do + { + $c--; + $escaped_value = $this->escape($binds[$c]); + if (is_array($escaped_value)) + { + $escaped_value = '('.implode(',', $escaped_value).')'; + } + $sql = substr_replace($sql, $escaped_value, $matches[0][$c][1], $ml); + } + while ($c !== 0); + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Determines if a query is a "write" type. + * + * @param string An SQL query string + * @return bool + */ + public function is_write_type($sql) + { + return (bool) preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX)\s/i', $sql); + } + + // -------------------------------------------------------------------- + + /** + * Calculate the aggregate query elapsed time + * + * @param int The number of decimal places + * @return string + */ + public function elapsed_time($decimals = 6) + { + return number_format($this->benchmark, $decimals); + } + + // -------------------------------------------------------------------- + + /** + * Returns the total number of queries + * + * @return int + */ + public function total_queries() + { + return $this->query_count; + } + + // -------------------------------------------------------------------- + + /** + * Returns the last query that was executed + * + * @return string + */ + public function last_query() + { + return end($this->queries); + } + + // -------------------------------------------------------------------- + + /** + * "Smart" Escape String + * + * Escapes data based on type + * Sets boolean and null types + * + * @param string + * @return mixed + */ + public function escape($str) + { + if (is_array($str)) + { + $str = array_map(array(&$this, 'escape'), $str); + return $str; + } + elseif (is_string($str) OR (is_object($str) && method_exists($str, '__toString'))) + { + return "'".$this->escape_str($str)."'"; + } + elseif (is_bool($str)) + { + return ($str === FALSE) ? 0 : 1; + } + elseif ($str === NULL) + { + return 'NULL'; + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @param string|string[] $str Input string + * @param bool $like Whether or not the string will be used in a LIKE condition + * @return string + */ + public function escape_str($str, $like = FALSE) + { + if (is_array($str)) + { + foreach ($str as $key => $val) + { + $str[$key] = $this->escape_str($val, $like); + } + + return $str; + } + + $str = $this->_escape_str($str); + + // escape LIKE condition wildcards + if ($like === TRUE) + { + return str_replace( + array($this->_like_escape_chr, '%', '_'), + array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'), + $str + ); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Escape LIKE String + * + * Calls the individual driver for platform + * specific escaping for LIKE conditions + * + * @param string|string[] + * @return mixed + */ + public function escape_like_str($str) + { + return $this->escape_str($str, TRUE); + } + + // -------------------------------------------------------------------- + + /** + * Platform-dependant string escape + * + * @param string + * @return string + */ + protected function _escape_str($str) + { + return str_replace("'", "''", remove_invisible_characters($str)); + } + + // -------------------------------------------------------------------- + + /** + * Primary + * + * Retrieves the primary key. It assumes that the row in the first + * position is the primary key + * + * @param string $table Table name + * @return string + */ + public function primary($table) + { + $fields = $this->list_fields($table); + return is_array($fields) ? current($fields) : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @param string + * @return int + */ + public function count_all($table = '') + { + if ($table === '') + { + return 0; + } + + $query = $this->query($this->_count_string.$this->escape_identifiers('numrows').' FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE)); + if ($query->num_rows() === 0) + { + return 0; + } + + $query = $query->row(); + $this->_reset_select(); + return (int) $query->numrows; + } + + // -------------------------------------------------------------------- + + /** + * Returns an array of table names + * + * @param string $constrain_by_prefix = FALSE + * @return array + */ + public function list_tables($constrain_by_prefix = FALSE) + { + // Is there a cached result? + if (isset($this->data_cache['table_names'])) + { + return $this->data_cache['table_names']; + } + + if (FALSE === ($sql = $this->_list_tables($constrain_by_prefix))) + { + return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE; + } + + $this->data_cache['table_names'] = array(); + $query = $this->query($sql); + + foreach ($query->result_array() as $row) + { + // Do we know from which column to get the table name? + if ( ! isset($key)) + { + if (isset($row['table_name'])) + { + $key = 'table_name'; + } + elseif (isset($row['TABLE_NAME'])) + { + $key = 'TABLE_NAME'; + } + else + { + /* We have no other choice but to just get the first element's key. + * Due to array_shift() accepting its argument by reference, if + * E_STRICT is on, this would trigger a warning. So we'll have to + * assign it first. + */ + $key = array_keys($row); + $key = array_shift($key); + } + } + + $this->data_cache['table_names'][] = $row[$key]; + } + + return $this->data_cache['table_names']; + } + + // -------------------------------------------------------------------- + + /** + * Determine if a particular table exists + * + * @param string $table_name + * @return bool + */ + public function table_exists($table_name) + { + return in_array($this->protect_identifiers($table_name, TRUE, FALSE, FALSE), $this->list_tables()); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * @param string $table Table name + * @return array + */ + public function list_fields($table) + { + // Is there a cached result? + if (isset($this->data_cache['field_names'][$table])) + { + return $this->data_cache['field_names'][$table]; + } + + if (FALSE === ($sql = $this->_list_columns($table))) + { + return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE; + } + + $query = $this->query($sql); + $this->data_cache['field_names'][$table] = array(); + + foreach ($query->result_array() as $row) + { + // Do we know from where to get the column's name? + if ( ! isset($key)) + { + if (isset($row['column_name'])) + { + $key = 'column_name'; + } + elseif (isset($row['COLUMN_NAME'])) + { + $key = 'COLUMN_NAME'; + } + else + { + // We have no other choice but to just get the first element's key. + $key = key($row); + } + } + + $this->data_cache['field_names'][$table][] = $row[$key]; + } + + return $this->data_cache['field_names'][$table]; + } + + // -------------------------------------------------------------------- + + /** + * Determine if a particular field exists + * + * @param string + * @param string + * @return bool + */ + public function field_exists($field_name, $table_name) + { + return in_array($field_name, $this->list_fields($table_name)); + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table the table name + * @return array + */ + public function field_data($table) + { + $query = $this->query($this->_field_data($this->protect_identifiers($table, TRUE, NULL, FALSE))); + return ($query) ? $query->field_data() : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @param mixed + * @return mixed + */ + public function escape_identifiers($item) + { + if ($this->_escape_char === '' OR empty($item) OR in_array($item, $this->_reserved_identifiers)) + { + return $item; + } + elseif (is_array($item)) + { + foreach ($item as $key => $value) + { + $item[$key] = $this->escape_identifiers($value); + } + + return $item; + } + // Avoid breaking functions and literal values inside queries + elseif (ctype_digit($item) OR $item[0] === "'" OR ($this->_escape_char !== '"' && $item[0] === '"') OR strpos($item, '(') !== FALSE) + { + return $item; + } + + static $preg_ec = array(); + + if (empty($preg_ec)) + { + if (is_array($this->_escape_char)) + { + $preg_ec = array( + preg_quote($this->_escape_char[0], '/'), + preg_quote($this->_escape_char[1], '/'), + $this->_escape_char[0], + $this->_escape_char[1] + ); + } + else + { + $preg_ec[0] = $preg_ec[1] = preg_quote($this->_escape_char, '/'); + $preg_ec[2] = $preg_ec[3] = $this->_escape_char; + } + } + + foreach ($this->_reserved_identifiers as $id) + { + if (strpos($item, '.'.$id) !== FALSE) + { + return preg_replace('/'.$preg_ec[0].'?([^'.$preg_ec[1].'\.]+)'.$preg_ec[1].'?\./i', $preg_ec[2].'$1'.$preg_ec[3].'.', $item); + } + } + + return preg_replace('/'.$preg_ec[0].'?([^'.$preg_ec[1].'\.]+)'.$preg_ec[1].'?(\.)?/i', $preg_ec[2].'$1'.$preg_ec[3].'$2', $item); + } + + // -------------------------------------------------------------------- + + /** + * Generate an insert string + * + * @param string the table upon which the query will be performed + * @param array an associative array data of key/values + * @return string + */ + public function insert_string($table, $data) + { + $fields = $values = array(); + + foreach ($data as $key => $val) + { + $fields[] = $this->escape_identifiers($key); + $values[] = $this->escape($val); + } + + return $this->_insert($this->protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values); + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + protected function _insert($table, $keys, $values) + { + return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')'; + } + + // -------------------------------------------------------------------- + + /** + * Generate an update string + * + * @param string the table upon which the query will be performed + * @param array an associative array data of key/values + * @param mixed the "where" statement + * @return string + */ + public function update_string($table, $data, $where) + { + if (empty($where)) + { + return FALSE; + } + + $this->where($where); + + $fields = array(); + foreach ($data as $key => $val) + { + $fields[$this->protect_identifiers($key)] = $this->escape($val); + } + + $sql = $this->_update($this->protect_identifiers($table, TRUE, NULL, FALSE), $fields); + $this->_reset_write(); + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @param string the table name + * @param array the update data + * @return string + */ + protected function _update($table, $values) + { + foreach ($values as $key => $val) + { + $valstr[] = $key.' = '.$val; + } + + return 'UPDATE '.$table.' SET '.implode(', ', $valstr) + .$this->_compile_wh('qb_where') + .$this->_compile_order_by() + .($this->qb_limit ? ' LIMIT '.$this->qb_limit : ''); + } + + // -------------------------------------------------------------------- + + /** + * Tests whether the string has an SQL operator + * + * @param string + * @return bool + */ + protected function _has_operator($str) + { + return (bool) preg_match('/(<|>|!|=|\sIS NULL|\sIS NOT NULL|\sEXISTS|\sBETWEEN|\sLIKE|\sIN\s*\(|\s)/i', trim($str)); + } + + // -------------------------------------------------------------------- + + /** + * Returns the SQL string operator + * + * @param string + * @return string + */ + protected function _get_operator($str) + { + static $_operators; + + if (empty($_operators)) + { + $_les = ($this->_like_escape_str !== '') + ? '\s+'.preg_quote(trim(sprintf($this->_like_escape_str, $this->_like_escape_chr)), '/') + : ''; + $_operators = array( + '\s*(?:<|>|!)?=\s*', // =, <=, >=, != + '\s*<>?\s*', // <, <> + '\s*>\s*', // > + '\s+IS NULL', // IS NULL + '\s+IS NOT NULL', // IS NOT NULL + '\s+EXISTS\s*\([^\)]+\)', // EXISTS(sql) + '\s+NOT EXISTS\s*\([^\)]+\)', // NOT EXISTS(sql) + '\s+BETWEEN\s+', // BETWEEN value AND value + '\s+IN\s*\([^\)]+\)', // IN(list) + '\s+NOT IN\s*\([^\)]+\)', // NOT IN (list) + '\s+LIKE\s+\S.*('.$_les.')?', // LIKE 'expr'[ ESCAPE '%s'] + '\s+NOT LIKE\s+\S.*('.$_les.')?' // NOT LIKE 'expr'[ ESCAPE '%s'] + ); + + } + + return preg_match('/'.implode('|', $_operators).'/i', $str, $match) + ? $match[0] : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Enables a native PHP function to be run, using a platform agnostic wrapper. + * + * @param string $function Function name + * @return mixed + */ + public function call_function($function) + { + $driver = ($this->dbdriver === 'postgre') ? 'pg_' : $this->dbdriver.'_'; + + if (FALSE === strpos($driver, $function)) + { + $function = $driver.$function; + } + + if ( ! function_exists($function)) + { + return ($this->db_debug) ? $this->display_error('db_unsupported_function') : FALSE; + } + + return (func_num_args() > 1) + ? call_user_func_array($function, array_slice(func_get_args(), 1)) + : call_user_func($function); + } + + // -------------------------------------------------------------------- + + /** + * Set Cache Directory Path + * + * @param string the path to the cache directory + * @return void + */ + public function cache_set_path($path = '') + { + $this->cachedir = $path; + } + + // -------------------------------------------------------------------- + + /** + * Enable Query Caching + * + * @return bool cache_on value + */ + public function cache_on() + { + return $this->cache_on = TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Disable Query Caching + * + * @return bool cache_on value + */ + public function cache_off() + { + return $this->cache_on = FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Delete the cache files associated with a particular URI + * + * @param string $segment_one = '' + * @param string $segment_two = '' + * @return bool + */ + public function cache_delete($segment_one = '', $segment_two = '') + { + return $this->_cache_init() + ? $this->CACHE->delete($segment_one, $segment_two) + : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Delete All cache files + * + * @return bool + */ + public function cache_delete_all() + { + return $this->_cache_init() + ? $this->CACHE->delete_all() + : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Initialize the Cache Class + * + * @return bool + */ + protected function _cache_init() + { + if ( ! class_exists('CI_DB_Cache', FALSE)) + { + require_once(BASEPATH.'database/DB_cache.php'); + } + elseif (is_object($this->CACHE)) + { + return TRUE; + } + + $this->CACHE = new CI_DB_Cache($this); // pass db object to support multiple db connections and returned db objects + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @return void + */ + public function close() + { + if ($this->conn_id) + { + $this->_close(); + $this->conn_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * This method would be overridden by most of the drivers. + * + * @return void + */ + protected function _close() + { + $this->conn_id = FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Display an error message + * + * @param string the error message + * @param string any "swap" values + * @param bool whether to localize the message + * @return string sends the application/views/errors/error_db.php template + */ + public function display_error($error = '', $swap = '', $native = FALSE) + { + $LANG =& load_class('Lang', 'core'); + $LANG->load('db'); + + $heading = $LANG->line('db_error_heading'); + + if ($native === TRUE) + { + $message = (array) $error; + } + else + { + $message = is_array($error) ? $error : array(str_replace('%s', $swap, $LANG->line($error))); + } + + // Find the most likely culprit of the error by going through + // the backtrace until the source file is no longer in the + // database folder. + $trace = debug_backtrace(); + foreach ($trace as $call) + { + if (isset($call['file'], $call['class'])) + { + // We'll need this on Windows, as APPPATH and BASEPATH will always use forward slashes + if (DIRECTORY_SEPARATOR !== '/') + { + $call['file'] = str_replace('\\', '/', $call['file']); + } + + if (strpos($call['file'], BASEPATH.'database') === FALSE && strpos($call['class'], 'Loader') === FALSE) + { + // Found it - use a relative path for safety + $message[] = 'Filename: '.str_replace(array(APPPATH, BASEPATH), '', $call['file']); + $message[] = 'Line Number: '.$call['line']; + break; + } + } + } + + $error =& load_class('Exceptions', 'core'); + echo $error->show_error($heading, $message, 'error_db'); + exit(8); // EXIT_DATABASE + } + + // -------------------------------------------------------------------- + + /** + * Protect Identifiers + * + * This function is used extensively by the Query Builder class, and by + * a couple functions in this class. + * It takes a column or table name (optionally with an alias) and inserts + * the table prefix onto it. Some logic is necessary in order to deal with + * column names that include the path. Consider a query like this: + * + * SELECT * FROM hostname.database.table.column AS c FROM hostname.database.table + * + * Or a query with aliasing: + * + * SELECT m.member_id, m.member_name FROM members AS m + * + * Since the column name can include up to four segments (host, DB, table, column) + * or also have an alias prefix, we need to do a bit of work to figure this out and + * insert the table prefix (if it exists) in the proper position, and escape only + * the correct identifiers. + * + * @param string + * @param bool + * @param mixed + * @param bool + * @return string + */ + public function protect_identifiers($item, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE) + { + if ( ! is_bool($protect_identifiers)) + { + $protect_identifiers = $this->_protect_identifiers; + } + + if (is_array($item)) + { + $escaped_array = array(); + foreach ($item as $k => $v) + { + $escaped_array[$this->protect_identifiers($k)] = $this->protect_identifiers($v, $prefix_single, $protect_identifiers, $field_exists); + } + + return $escaped_array; + } + + // This is basically a bug fix for queries that use MAX, MIN, etc. + // If a parenthesis is found we know that we do not need to + // escape the data or add a prefix. There's probably a more graceful + // way to deal with this, but I'm not thinking of it -- Rick + // + // Added exception for single quotes as well, we don't want to alter + // literal strings. -- Narf + if (strcspn($item, "()'") !== strlen($item)) + { + return $item; + } + + // Convert tabs or multiple spaces into single spaces + $item = preg_replace('/\s+/', ' ', trim($item)); + + // If the item has an alias declaration we remove it and set it aside. + // Note: strripos() is used in order to support spaces in table names + if ($offset = strripos($item, ' AS ')) + { + $alias = ($protect_identifiers) + ? substr($item, $offset, 4).$this->escape_identifiers(substr($item, $offset + 4)) + : substr($item, $offset); + $item = substr($item, 0, $offset); + } + elseif ($offset = strrpos($item, ' ')) + { + $alias = ($protect_identifiers) + ? ' '.$this->escape_identifiers(substr($item, $offset + 1)) + : substr($item, $offset); + $item = substr($item, 0, $offset); + } + else + { + $alias = ''; + } + + // Break the string apart if it contains periods, then insert the table prefix + // in the correct location, assuming the period doesn't indicate that we're dealing + // with an alias. While we're at it, we will escape the components + if (strpos($item, '.') !== FALSE) + { + $parts = explode('.', $item); + + // Does the first segment of the exploded item match + // one of the aliases previously identified? If so, + // we have nothing more to do other than escape the item + // + // NOTE: The ! empty() condition prevents this method + // from breaking when QB isn't enabled. + if ( ! empty($this->qb_aliased_tables) && in_array($parts[0], $this->qb_aliased_tables)) + { + if ($protect_identifiers === TRUE) + { + foreach ($parts as $key => $val) + { + if ( ! in_array($val, $this->_reserved_identifiers)) + { + $parts[$key] = $this->escape_identifiers($val); + } + } + + $item = implode('.', $parts); + } + + return $item.$alias; + } + + // Is there a table prefix defined in the config file? If not, no need to do anything + if ($this->dbprefix !== '') + { + // We now add the table prefix based on some logic. + // Do we have 4 segments (hostname.database.table.column)? + // If so, we add the table prefix to the column name in the 3rd segment. + if (isset($parts[3])) + { + $i = 2; + } + // Do we have 3 segments (database.table.column)? + // If so, we add the table prefix to the column name in 2nd position + elseif (isset($parts[2])) + { + $i = 1; + } + // Do we have 2 segments (table.column)? + // If so, we add the table prefix to the column name in 1st segment + else + { + $i = 0; + } + + // This flag is set when the supplied $item does not contain a field name. + // This can happen when this function is being called from a JOIN. + if ($field_exists === FALSE) + { + $i++; + } + + // Verify table prefix and replace if necessary + if ($this->swap_pre !== '' && strpos($parts[$i], $this->swap_pre) === 0) + { + $parts[$i] = preg_replace('/^'.$this->swap_pre.'(\S+?)/', $this->dbprefix.'\\1', $parts[$i]); + } + // We only add the table prefix if it does not already exist + elseif (strpos($parts[$i], $this->dbprefix) !== 0) + { + $parts[$i] = $this->dbprefix.$parts[$i]; + } + + // Put the parts back together + $item = implode('.', $parts); + } + + if ($protect_identifiers === TRUE) + { + $item = $this->escape_identifiers($item); + } + + return $item.$alias; + } + + // Is there a table prefix? If not, no need to insert it + if ($this->dbprefix !== '') + { + // Verify table prefix and replace if necessary + if ($this->swap_pre !== '' && strpos($item, $this->swap_pre) === 0) + { + $item = preg_replace('/^'.$this->swap_pre.'(\S+?)/', $this->dbprefix.'\\1', $item); + } + // Do we prefix an item with no segments? + elseif ($prefix_single === TRUE && strpos($item, $this->dbprefix) !== 0) + { + $item = $this->dbprefix.$item; + } + } + + if ($protect_identifiers === TRUE && ! in_array($item, $this->_reserved_identifiers)) + { + $item = $this->escape_identifiers($item); + } + + return $item.$alias; + } + + // -------------------------------------------------------------------- + + /** + * Dummy method that allows Query Builder class to be disabled + * and keep count_all() working. + * + * @return void + */ + protected function _reset_select() + { + } + +} diff --git a/ci-3.0/system/database/DB_forge.php b/ci-3.0/system/database/DB_forge.php new file mode 100755 index 000000000..f9cf76a14 --- /dev/null +++ b/ci-3.0/system/database/DB_forge.php @@ -0,0 +1,1036 @@ +db =& $db; + log_message('info', 'Database Forge Class Initialized'); + } + + // -------------------------------------------------------------------- + + /** + * Create database + * + * @param string $db_name + * @return bool + */ + public function create_database($db_name) + { + if ($this->_create_database === FALSE) + { + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + elseif ( ! $this->db->query(sprintf($this->_create_database, $db_name, $this->db->char_set, $this->db->dbcollat))) + { + return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE; + } + + if ( ! empty($this->db->data_cache['db_names'])) + { + $this->db->data_cache['db_names'][] = $db_name; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @param string $db_name + * @return bool + */ + public function drop_database($db_name) + { + if ($this->_drop_database === FALSE) + { + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + elseif ( ! $this->db->query(sprintf($this->_drop_database, $db_name))) + { + return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE; + } + + if ( ! empty($this->db->data_cache['db_names'])) + { + $key = array_search(strtolower($db_name), array_map('strtolower', $this->db->data_cache['db_names']), TRUE); + if ($key !== FALSE) + { + unset($this->db->data_cache['db_names'][$key]); + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Add Key + * + * @param string $key + * @param bool $primary + * @return CI_DB_forge + */ + public function add_key($key, $primary = FALSE) + { + // DO NOT change this! This condition is only applicable + // for PRIMARY keys because you can only have one such, + // and therefore all fields you add to it will be included + // in the same, composite PRIMARY KEY. + // + // It's not the same for regular indexes. + if ($primary === TRUE && is_array($key)) + { + foreach ($key as $one) + { + $this->add_key($one, $primary); + } + + return $this; + } + + if ($primary === TRUE) + { + $this->primary_keys[] = $key; + } + else + { + $this->keys[] = $key; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Add Field + * + * @param array $field + * @return CI_DB_forge + */ + public function add_field($field) + { + if (is_string($field)) + { + if ($field === 'id') + { + $this->add_field(array( + 'id' => array( + 'type' => 'INT', + 'constraint' => 9, + 'auto_increment' => TRUE + ) + )); + $this->add_key('id', TRUE); + } + else + { + if (strpos($field, ' ') === FALSE) + { + show_error('Field information is required for that operation.'); + } + + $this->fields[] = $field; + } + } + + if (is_array($field)) + { + $this->fields = array_merge($this->fields, $field); + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @param string $table Table name + * @param bool $if_not_exists Whether to add IF NOT EXISTS condition + * @param array $attributes Associative array of table attributes + * @return bool + */ + public function create_table($table, $if_not_exists = FALSE, array $attributes = array()) + { + if ($table === '') + { + show_error('A table name is required for that operation.'); + } + else + { + $table = $this->db->dbprefix.$table; + } + + if (count($this->fields) === 0) + { + show_error('Field information is required.'); + } + + $sql = $this->_create_table($table, $if_not_exists, $attributes); + + if (is_bool($sql)) + { + $this->_reset(); + if ($sql === FALSE) + { + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + } + + if (($result = $this->db->query($sql)) !== FALSE) + { + empty($this->db->data_cache['table_names']) OR $this->db->data_cache['table_names'][] = $table; + + // Most databases don't support creating indexes from within the CREATE TABLE statement + if ( ! empty($this->keys)) + { + for ($i = 0, $sqls = $this->_process_indexes($table), $c = count($sqls); $i < $c; $i++) + { + $this->db->query($sqls[$i]); + } + } + } + + $this->_reset(); + return $result; + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @param string $table Table name + * @param bool $if_not_exists Whether to add 'IF NOT EXISTS' condition + * @param array $attributes Associative array of table attributes + * @return mixed + */ + protected function _create_table($table, $if_not_exists, $attributes) + { + if ($if_not_exists === TRUE && $this->_create_table_if === FALSE) + { + if ($this->db->table_exists($table)) + { + return TRUE; + } + else + { + $if_not_exists = FALSE; + } + } + + $sql = ($if_not_exists) + ? sprintf($this->_create_table_if, $this->db->escape_identifiers($table)) + : 'CREATE TABLE'; + + $columns = $this->_process_fields(TRUE); + for ($i = 0, $c = count($columns); $i < $c; $i++) + { + $columns[$i] = ($columns[$i]['_literal'] !== FALSE) + ? "\n\t".$columns[$i]['_literal'] + : "\n\t".$this->_process_column($columns[$i]); + } + + $columns = implode(',', $columns) + .$this->_process_primary_keys($table); + + // Are indexes created from within the CREATE TABLE statement? (e.g. in MySQL) + if ($this->_create_table_keys === TRUE) + { + $columns .= $this->_process_indexes($table); + } + + // _create_table will usually have the following format: "%s %s (%s\n)" + $sql = sprintf($this->_create_table.'%s', + $sql, + $this->db->escape_identifiers($table), + $columns, + $this->_create_table_attr($attributes) + ); + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * CREATE TABLE attributes + * + * @param array $attributes Associative array of table attributes + * @return string + */ + protected function _create_table_attr($attributes) + { + $sql = ''; + + foreach (array_keys($attributes) as $key) + { + if (is_string($key)) + { + $sql .= ' '.strtoupper($key).' '.$attributes[$key]; + } + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @param string $table_name Table name + * @param bool $if_exists Whether to add an IF EXISTS condition + * @return bool + */ + public function drop_table($table_name, $if_exists = FALSE) + { + if ($table_name === '') + { + return ($this->db->db_debug) ? $this->db->display_error('db_table_name_required') : FALSE; + } + + if (($query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists)) === TRUE) + { + return TRUE; + } + + $query = $this->db->query($query); + + // Update table list cache + if ($query && ! empty($this->db->data_cache['table_names'])) + { + $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE); + if ($key !== FALSE) + { + unset($this->db->data_cache['table_names'][$key]); + } + } + + return $query; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * Generates a platform-specific DROP TABLE string + * + * @param string $table Table name + * @param bool $if_exists Whether to add an IF EXISTS condition + * @return string + */ + protected function _drop_table($table, $if_exists) + { + $sql = 'DROP TABLE'; + + if ($if_exists) + { + if ($this->_drop_table_if === FALSE) + { + if ( ! $this->db->table_exists($table)) + { + return TRUE; + } + } + else + { + $sql = sprintf($this->_drop_table_if, $this->db->escape_identifiers($table)); + } + } + + return $sql.' '.$this->db->escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * Rename Table + * + * @param string $table_name Old table name + * @param string $new_table_name New table name + * @return bool + */ + public function rename_table($table_name, $new_table_name) + { + if ($table_name === '' OR $new_table_name === '') + { + show_error('A table name is required for that operation.'); + return FALSE; + } + elseif ($this->_rename_table === FALSE) + { + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + + $result = $this->db->query(sprintf($this->_rename_table, + $this->db->escape_identifiers($this->db->dbprefix.$table_name), + $this->db->escape_identifiers($this->db->dbprefix.$new_table_name)) + ); + + if ($result && ! empty($this->db->data_cache['table_names'])) + { + $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE); + if ($key !== FALSE) + { + $this->db->data_cache['table_names'][$key] = $this->db->dbprefix.$new_table_name; + } + } + + return $result; + } + + // -------------------------------------------------------------------- + + /** + * Column Add + * + * @todo Remove deprecated $_after option in 3.1+ + * @param string $table Table name + * @param array $field Column definition + * @param string $_after Column for AFTER clause (deprecated) + * @return bool + */ + public function add_column($table, $field, $_after = NULL) + { + // Work-around for literal column definitions + is_array($field) OR $field = array($field); + + foreach (array_keys($field) as $k) + { + // Backwards-compatibility work-around for MySQL/CUBRID AFTER clause (remove in 3.1+) + if ($_after !== NULL && is_array($field[$k]) && ! isset($field[$k]['after'])) + { + $field[$k]['after'] = $_after; + } + + $this->add_field(array($k => $field[$k])); + } + + $sqls = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->_process_fields()); + $this->_reset(); + if ($sqls === FALSE) + { + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + + for ($i = 0, $c = count($sqls); $i < $c; $i++) + { + if ($this->db->query($sqls[$i]) === FALSE) + { + return FALSE; + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Column Drop + * + * @param string $table Table name + * @param string $column_name Column name + * @return bool + */ + public function drop_column($table, $column_name) + { + $sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name); + if ($sql === FALSE) + { + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Column Modify + * + * @param string $table Table name + * @param string $field Column definition + * @return bool + */ + public function modify_column($table, $field) + { + // Work-around for literal column definitions + is_array($field) OR $field = array($field); + + foreach (array_keys($field) as $k) + { + $this->add_field(array($k => $field[$k])); + } + + if (count($this->fields) === 0) + { + show_error('Field information is required.'); + } + + $sqls = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->_process_fields()); + $this->_reset(); + if ($sqls === FALSE) + { + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + + for ($i = 0, $c = count($sqls); $i < $c; $i++) + { + if ($this->db->query($sqls[$i]) === FALSE) + { + return FALSE; + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * ALTER TABLE + * + * @param string $alter_type ALTER type + * @param string $table Table name + * @param mixed $field Column definition + * @return string|string[] + */ + protected function _alter_table($alter_type, $table, $field) + { + $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' '; + + // DROP has everything it needs now. + if ($alter_type === 'DROP') + { + return $sql.'DROP COLUMN '.$this->db->escape_identifiers($field); + } + + $sql .= ($alter_type === 'ADD') + ? 'ADD ' + : $alter_type.' COLUMN '; + + $sqls = array(); + for ($i = 0, $c = count($field); $i < $c; $i++) + { + $sqls[] = $sql + .($field[$i]['_literal'] !== FALSE ? $field[$i]['_literal'] : $this->_process_column($field[$i])); + } + + return $sqls; + } + + // -------------------------------------------------------------------- + + /** + * Process fields + * + * @param bool $create_table + * @return array + */ + protected function _process_fields($create_table = FALSE) + { + $fields = array(); + + foreach ($this->fields as $key => $attributes) + { + if (is_int($key) && ! is_array($attributes)) + { + $fields[] = array('_literal' => $attributes); + continue; + } + + $attributes = array_change_key_case($attributes, CASE_UPPER); + + if ($create_table === TRUE && empty($attributes['TYPE'])) + { + continue; + } + + isset($attributes['TYPE']) && $this->_attr_type($attributes); + + $field = array( + 'name' => $key, + 'new_name' => isset($attributes['NAME']) ? $attributes['NAME'] : NULL, + 'type' => isset($attributes['TYPE']) ? $attributes['TYPE'] : NULL, + 'length' => '', + 'unsigned' => '', + 'null' => '', + 'unique' => '', + 'default' => '', + 'auto_increment' => '', + '_literal' => FALSE + ); + + isset($attributes['TYPE']) && $this->_attr_unsigned($attributes, $field); + + if ($create_table === FALSE) + { + if (isset($attributes['AFTER'])) + { + $field['after'] = $attributes['AFTER']; + } + elseif (isset($attributes['FIRST'])) + { + $field['first'] = (bool) $attributes['FIRST']; + } + } + + $this->_attr_default($attributes, $field); + + if (isset($attributes['NULL'])) + { + if ($attributes['NULL'] === TRUE) + { + $field['null'] = empty($this->_null) ? '' : ' '.$this->_null; + } + else + { + $field['null'] = ' NOT NULL'; + } + } + elseif ($create_table === TRUE) + { + $field['null'] = ' NOT NULL'; + } + + $this->_attr_auto_increment($attributes, $field); + $this->_attr_unique($attributes, $field); + + if (isset($attributes['COMMENT'])) + { + $field['comment'] = $this->db->escape($attributes['COMMENT']); + } + + if (isset($attributes['TYPE']) && ! empty($attributes['CONSTRAINT'])) + { + switch (strtoupper($attributes['TYPE'])) + { + case 'ENUM': + case 'SET': + $attributes['CONSTRAINT'] = $this->db->escape($attributes['CONSTRAINT']); + $field['length'] = is_array($attributes['CONSTRAINT']) + ? "('".implode("','", $attributes['CONSTRAINT'])."')" + : '('.$attributes['CONSTRAINT'].')'; + break; + default: + $field['length'] = is_array($attributes['CONSTRAINT']) + ? '('.implode(',', $attributes['CONSTRAINT']).')' + : '('.$attributes['CONSTRAINT'].')'; + break; + } + } + + $fields[] = $field; + } + + return $fields; + } + + // -------------------------------------------------------------------- + + /** + * Process column + * + * @param array $field + * @return string + */ + protected function _process_column($field) + { + return $this->db->escape_identifiers($field['name']) + .' '.$field['type'].$field['length'] + .$field['unsigned'] + .$field['default'] + .$field['null'] + .$field['auto_increment'] + .$field['unique']; + } + + // -------------------------------------------------------------------- + + /** + * Field attribute TYPE + * + * Performs a data type mapping between different databases. + * + * @param array &$attributes + * @return void + */ + protected function _attr_type(&$attributes) + { + // Usually overridden by drivers + } + + // -------------------------------------------------------------------- + + /** + * Field attribute UNSIGNED + * + * Depending on the _unsigned property value: + * + * - TRUE will always set $field['unsigned'] to 'UNSIGNED' + * - FALSE will always set $field['unsigned'] to '' + * - array(TYPE) will set $field['unsigned'] to 'UNSIGNED', + * if $attributes['TYPE'] is found in the array + * - array(TYPE => UTYPE) will change $field['type'], + * from TYPE to UTYPE in case of a match + * + * @param array &$attributes + * @param array &$field + * @return void + */ + protected function _attr_unsigned(&$attributes, &$field) + { + if (empty($attributes['UNSIGNED']) OR $attributes['UNSIGNED'] !== TRUE) + { + return; + } + + // Reset the attribute in order to avoid issues if we do type conversion + $attributes['UNSIGNED'] = FALSE; + + if (is_array($this->_unsigned)) + { + foreach (array_keys($this->_unsigned) as $key) + { + if (is_int($key) && strcasecmp($attributes['TYPE'], $this->_unsigned[$key]) === 0) + { + $field['unsigned'] = ' UNSIGNED'; + return; + } + elseif (is_string($key) && strcasecmp($attributes['TYPE'], $key) === 0) + { + $field['type'] = $key; + return; + } + } + + return; + } + + $field['unsigned'] = ($this->_unsigned === TRUE) ? ' UNSIGNED' : ''; + } + + // -------------------------------------------------------------------- + + /** + * Field attribute DEFAULT + * + * @param array &$attributes + * @param array &$field + * @return void + */ + protected function _attr_default(&$attributes, &$field) + { + if ($this->_default === FALSE) + { + return; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + if ($attributes['DEFAULT'] === NULL) + { + $field['default'] = empty($this->_null) ? '' : $this->_default.$this->_null; + + // Override the NULL attribute if that's our default + $attributes['NULL'] = TRUE; + $field['null'] = empty($this->_null) ? '' : ' '.$this->_null; + } + else + { + $field['default'] = $this->_default.$this->db->escape($attributes['DEFAULT']); + } + } + } + + // -------------------------------------------------------------------- + + /** + * Field attribute UNIQUE + * + * @param array &$attributes + * @param array &$field + * @return void + */ + protected function _attr_unique(&$attributes, &$field) + { + if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE) + { + $field['unique'] = ' UNIQUE'; + } + } + + // -------------------------------------------------------------------- + + /** + * Field attribute AUTO_INCREMENT + * + * @param array &$attributes + * @param array &$field + * @return void + */ + protected function _attr_auto_increment(&$attributes, &$field) + { + if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE) + { + $field['auto_increment'] = ' AUTO_INCREMENT'; + } + } + + // -------------------------------------------------------------------- + + /** + * Process primary keys + * + * @param string $table Table name + * @return string + */ + protected function _process_primary_keys($table) + { + $sql = ''; + + for ($i = 0, $c = count($this->primary_keys); $i < $c; $i++) + { + if ( ! isset($this->fields[$this->primary_keys[$i]])) + { + unset($this->primary_keys[$i]); + } + } + + if (count($this->primary_keys) > 0) + { + $sql .= ",\n\tCONSTRAINT ".$this->db->escape_identifiers('pk_'.$table) + .' PRIMARY KEY('.implode(', ', $this->db->escape_identifiers($this->primary_keys)).')'; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Process indexes + * + * @param string $table + * @return string + */ + protected function _process_indexes($table) + { + $sqls = array(); + + for ($i = 0, $c = count($this->keys); $i < $c; $i++) + { + if (is_array($this->keys[$i])) + { + for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++) + { + if ( ! isset($this->fields[$this->keys[$i][$i2]])) + { + unset($this->keys[$i][$i2]); + continue; + } + } + } + elseif ( ! isset($this->fields[$this->keys[$i]])) + { + unset($this->keys[$i]); + continue; + } + + is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]); + + $sqls[] = 'CREATE INDEX '.$this->db->escape_identifiers($table.'_'.implode('_', $this->keys[$i])) + .' ON '.$this->db->escape_identifiers($table) + .' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).');'; + } + + return $sqls; + } + + // -------------------------------------------------------------------- + + /** + * Reset + * + * Resets table creation vars + * + * @return void + */ + protected function _reset() + { + $this->fields = $this->keys = $this->primary_keys = array(); + } + +} diff --git a/ci-3.0/system/database/DB_query_builder.php b/ci-3.0/system/database/DB_query_builder.php new file mode 100755 index 000000000..cf1100d27 --- /dev/null +++ b/ci-3.0/system/database/DB_query_builder.php @@ -0,0 +1,2748 @@ +_protect_identifiers; + + foreach ($select as $val) + { + $val = trim($val); + + if ($val !== '') + { + $this->qb_select[] = $val; + $this->qb_no_escape[] = $escape; + + if ($this->qb_caching === TRUE) + { + $this->qb_cache_select[] = $val; + $this->qb_cache_exists[] = 'select'; + $this->qb_cache_no_escape[] = $escape; + } + } + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Select Max + * + * Generates a SELECT MAX(field) portion of a query + * + * @param string the field + * @param string an alias + * @return CI_DB_query_builder + */ + public function select_max($select = '', $alias = '') + { + return $this->_max_min_avg_sum($select, $alias, 'MAX'); + } + + // -------------------------------------------------------------------- + + /** + * Select Min + * + * Generates a SELECT MIN(field) portion of a query + * + * @param string the field + * @param string an alias + * @return CI_DB_query_builder + */ + public function select_min($select = '', $alias = '') + { + return $this->_max_min_avg_sum($select, $alias, 'MIN'); + } + + // -------------------------------------------------------------------- + + /** + * Select Average + * + * Generates a SELECT AVG(field) portion of a query + * + * @param string the field + * @param string an alias + * @return CI_DB_query_builder + */ + public function select_avg($select = '', $alias = '') + { + return $this->_max_min_avg_sum($select, $alias, 'AVG'); + } + + // -------------------------------------------------------------------- + + /** + * Select Sum + * + * Generates a SELECT SUM(field) portion of a query + * + * @param string the field + * @param string an alias + * @return CI_DB_query_builder + */ + public function select_sum($select = '', $alias = '') + { + return $this->_max_min_avg_sum($select, $alias, 'SUM'); + } + + // -------------------------------------------------------------------- + + /** + * SELECT [MAX|MIN|AVG|SUM]() + * + * @used-by select_max() + * @used-by select_min() + * @used-by select_avg() + * @used-by select_sum() + * + * @param string $select Field name + * @param string $alias + * @param string $type + * @return CI_DB_query_builder + */ + protected function _max_min_avg_sum($select = '', $alias = '', $type = 'MAX') + { + if ( ! is_string($select) OR $select === '') + { + $this->display_error('db_invalid_query'); + } + + $type = strtoupper($type); + + if ( ! in_array($type, array('MAX', 'MIN', 'AVG', 'SUM'))) + { + show_error('Invalid function type: '.$type); + } + + if ($alias === '') + { + $alias = $this->_create_alias_from_table(trim($select)); + } + + $sql = $type.'('.$this->protect_identifiers(trim($select)).') AS '.$this->escape_identifiers(trim($alias)); + + $this->qb_select[] = $sql; + $this->qb_no_escape[] = NULL; + + if ($this->qb_caching === TRUE) + { + $this->qb_cache_select[] = $sql; + $this->qb_cache_exists[] = 'select'; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Determines the alias name based on the table + * + * @param string $item + * @return string + */ + protected function _create_alias_from_table($item) + { + if (strpos($item, '.') !== FALSE) + { + $item = explode('.', $item); + return end($item); + } + + return $item; + } + + // -------------------------------------------------------------------- + + /** + * DISTINCT + * + * Sets a flag which tells the query string compiler to add DISTINCT + * + * @param bool $val + * @return CI_DB_query_builder + */ + public function distinct($val = TRUE) + { + $this->qb_distinct = is_bool($val) ? $val : TRUE; + return $this; + } + + // -------------------------------------------------------------------- + + /** + * From + * + * Generates the FROM portion of the query + * + * @param mixed $from can be a string or array + * @return CI_DB_query_builder + */ + public function from($from) + { + foreach ((array) $from as $val) + { + if (strpos($val, ',') !== FALSE) + { + foreach (explode(',', $val) as $v) + { + $v = trim($v); + $this->_track_aliases($v); + + $this->qb_from[] = $v = $this->protect_identifiers($v, TRUE, NULL, FALSE); + + if ($this->qb_caching === TRUE) + { + $this->qb_cache_from[] = $v; + $this->qb_cache_exists[] = 'from'; + } + } + } + else + { + $val = trim($val); + + // Extract any aliases that might exist. We use this information + // in the protect_identifiers to know whether to add a table prefix + $this->_track_aliases($val); + + $this->qb_from[] = $val = $this->protect_identifiers($val, TRUE, NULL, FALSE); + + if ($this->qb_caching === TRUE) + { + $this->qb_cache_from[] = $val; + $this->qb_cache_exists[] = 'from'; + } + } + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * JOIN + * + * Generates the JOIN portion of the query + * + * @param string + * @param string the join condition + * @param string the type of join + * @param string whether not to try to escape identifiers + * @return CI_DB_query_builder + */ + public function join($table, $cond, $type = '', $escape = NULL) + { + if ($type !== '') + { + $type = strtoupper(trim($type)); + + if ( ! in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER'), TRUE)) + { + $type = ''; + } + else + { + $type .= ' '; + } + } + + // Extract any aliases that might exist. We use this information + // in the protect_identifiers to know whether to add a table prefix + $this->_track_aliases($table); + + is_bool($escape) OR $escape = $this->_protect_identifiers; + + // Split multiple conditions + if ($escape === TRUE && preg_match_all('/\sAND\s|\sOR\s/i', $cond, $m, PREG_OFFSET_CAPTURE)) + { + $newcond = ''; + $m[0][] = array('', strlen($cond)); + + for ($i = 0, $c = count($m[0]), $s = 0; + $i < $c; + $s = $m[0][$i][1] + strlen($m[0][$i][0]), $i++) + { + $temp = substr($cond, $s, ($m[0][$i][1] - $s)); + + $newcond .= preg_match("/([\[\]\w\.'-]+)(\s*[^\"\[`'\w]+\s*)(.+)/i", $temp, $match) + ? $this->protect_identifiers($match[1]).$match[2].$this->protect_identifiers($match[3]) + : $temp; + + $newcond .= $m[0][$i][0]; + } + + $cond = ' ON '.$newcond; + } + // Split apart the condition and protect the identifiers + elseif ($escape === TRUE && preg_match("/([\[\]\w\.'-]+)(\s*[^\"\[`'\w]+\s*)(.+)/i", $cond, $match)) + { + $cond = ' ON '.$this->protect_identifiers($match[1]).$match[2].$this->protect_identifiers($match[3]); + } + elseif ( ! $this->_has_operator($cond)) + { + $cond = ' USING ('.($escape ? $this->escape_identifiers($cond) : $cond).')'; + } + else + { + $cond = ' ON '.$cond; + } + + // Do we want to escape the table name? + if ($escape === TRUE) + { + $table = $this->protect_identifiers($table, TRUE, NULL, FALSE); + } + + // Assemble the JOIN statement + $this->qb_join[] = $join = $type.'JOIN '.$table.$cond; + + if ($this->qb_caching === TRUE) + { + $this->qb_cache_join[] = $join; + $this->qb_cache_exists[] = 'join'; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * WHERE + * + * Generates the WHERE portion of the query. + * Separates multiple calls with 'AND'. + * + * @param mixed + * @param mixed + * @param bool + * @return CI_DB_query_builder + */ + public function where($key, $value = NULL, $escape = NULL) + { + return $this->_wh('qb_where', $key, $value, 'AND ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * OR WHERE + * + * Generates the WHERE portion of the query. + * Separates multiple calls with 'OR'. + * + * @param mixed + * @param mixed + * @param bool + * @return CI_DB_query_builder + */ + public function or_where($key, $value = NULL, $escape = NULL) + { + return $this->_wh('qb_where', $key, $value, 'OR ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * WHERE, HAVING + * + * @used-by where() + * @used-by or_where() + * @used-by having() + * @used-by or_having() + * + * @param string $qb_key 'qb_where' or 'qb_having' + * @param mixed $key + * @param mixed $value + * @param string $type + * @param bool $escape + * @return CI_DB_query_builder + */ + protected function _wh($qb_key, $key, $value = NULL, $type = 'AND ', $escape = NULL) + { + $qb_cache_key = ($qb_key === 'qb_having') ? 'qb_cache_having' : 'qb_cache_where'; + + if ( ! is_array($key)) + { + $key = array($key => $value); + } + + // If the escape value was not set will base it on the global setting + is_bool($escape) OR $escape = $this->_protect_identifiers; + + foreach ($key as $k => $v) + { + $prefix = (count($this->$qb_key) === 0 && count($this->$qb_cache_key) === 0) + ? $this->_group_get_type('') + : $this->_group_get_type($type); + + if ($v !== NULL) + { + if ($escape === TRUE) + { + $v = ' '.$this->escape($v); + } + + if ( ! $this->_has_operator($k)) + { + $k .= ' = '; + } + } + elseif ( ! $this->_has_operator($k)) + { + // value appears not to have been set, assign the test to IS NULL + $k .= ' IS NULL'; + } + elseif (preg_match('/\s*(!?=|<>|IS(?:\s+NOT)?)\s*$/i', $k, $match, PREG_OFFSET_CAPTURE)) + { + $k = substr($k, 0, $match[0][1]).($match[1][0] === '=' ? ' IS NULL' : ' IS NOT NULL'); + } + + $this->{$qb_key}[] = array('condition' => $prefix.$k.$v, 'escape' => $escape); + if ($this->qb_caching === TRUE) + { + $this->{$qb_cache_key}[] = array('condition' => $prefix.$k.$v, 'escape' => $escape); + $this->qb_cache_exists[] = substr($qb_key, 3); + } + + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * WHERE IN + * + * Generates a WHERE field IN('item', 'item') SQL query, + * joined with 'AND' if appropriate. + * + * @param string $key The field to search + * @param array $values The values searched on + * @param bool $escape + * @return CI_DB_query_builder + */ + public function where_in($key = NULL, $values = NULL, $escape = NULL) + { + return $this->_where_in($key, $values, FALSE, 'AND ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * OR WHERE IN + * + * Generates a WHERE field IN('item', 'item') SQL query, + * joined with 'OR' if appropriate. + * + * @param string $key The field to search + * @param array $values The values searched on + * @param bool $escape + * @return CI_DB_query_builder + */ + public function or_where_in($key = NULL, $values = NULL, $escape = NULL) + { + return $this->_where_in($key, $values, FALSE, 'OR ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * WHERE NOT IN + * + * Generates a WHERE field NOT IN('item', 'item') SQL query, + * joined with 'AND' if appropriate. + * + * @param string $key The field to search + * @param array $values The values searched on + * @param bool $escape + * @return CI_DB_query_builder + */ + public function where_not_in($key = NULL, $values = NULL, $escape = NULL) + { + return $this->_where_in($key, $values, TRUE, 'AND ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * OR WHERE NOT IN + * + * Generates a WHERE field NOT IN('item', 'item') SQL query, + * joined with 'OR' if appropriate. + * + * @param string $key The field to search + * @param array $values The values searched on + * @param bool $escape + * @return CI_DB_query_builder + */ + public function or_where_not_in($key = NULL, $values = NULL, $escape = NULL) + { + return $this->_where_in($key, $values, TRUE, 'OR ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * Internal WHERE IN + * + * @used-by where_in() + * @used-by or_where_in() + * @used-by where_not_in() + * @used-by or_where_not_in() + * + * @param string $key The field to search + * @param array $values The values searched on + * @param bool $not If the statement would be IN or NOT IN + * @param string $type + * @param bool $escape + * @return CI_DB_query_builder + */ + protected function _where_in($key = NULL, $values = NULL, $not = FALSE, $type = 'AND ', $escape = NULL) + { + if ($key === NULL OR $values === NULL) + { + return $this; + } + + if ( ! is_array($values)) + { + $values = array($values); + } + + is_bool($escape) OR $escape = $this->_protect_identifiers; + + $not = ($not) ? ' NOT' : ''; + + if ($escape === TRUE) + { + $where_in = array(); + foreach ($values as $value) + { + $where_in[] = $this->escape($value); + } + } + else + { + $where_in = array_values($values); + } + + $prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) + ? $this->_group_get_type('') + : $this->_group_get_type($type); + + $where_in = array( + 'condition' => $prefix.$key.$not.' IN('.implode(', ', $where_in).')', + 'escape' => $escape + ); + + $this->qb_where[] = $where_in; + if ($this->qb_caching === TRUE) + { + $this->qb_cache_where[] = $where_in; + $this->qb_cache_exists[] = 'where'; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * LIKE + * + * Generates a %LIKE% portion of the query. + * Separates multiple calls with 'AND'. + * + * @param mixed $field + * @param string $match + * @param string $side + * @param bool $escape + * @return CI_DB_query_builder + */ + public function like($field, $match = '', $side = 'both', $escape = NULL) + { + return $this->_like($field, $match, 'AND ', $side, '', $escape); + } + + // -------------------------------------------------------------------- + + /** + * NOT LIKE + * + * Generates a NOT LIKE portion of the query. + * Separates multiple calls with 'AND'. + * + * @param mixed $field + * @param string $match + * @param string $side + * @param bool $escape + * @return CI_DB_query_builder + */ + public function not_like($field, $match = '', $side = 'both', $escape = NULL) + { + return $this->_like($field, $match, 'AND ', $side, 'NOT', $escape); + } + + // -------------------------------------------------------------------- + + /** + * OR LIKE + * + * Generates a %LIKE% portion of the query. + * Separates multiple calls with 'OR'. + * + * @param mixed $field + * @param string $match + * @param string $side + * @param bool $escape + * @return CI_DB_query_builder + */ + public function or_like($field, $match = '', $side = 'both', $escape = NULL) + { + return $this->_like($field, $match, 'OR ', $side, '', $escape); + } + + // -------------------------------------------------------------------- + + /** + * OR NOT LIKE + * + * Generates a NOT LIKE portion of the query. + * Separates multiple calls with 'OR'. + * + * @param mixed $field + * @param string $match + * @param string $side + * @param bool $escape + * @return CI_DB_query_builder + */ + public function or_not_like($field, $match = '', $side = 'both', $escape = NULL) + { + return $this->_like($field, $match, 'OR ', $side, 'NOT', $escape); + } + + // -------------------------------------------------------------------- + + /** + * Internal LIKE + * + * @used-by like() + * @used-by or_like() + * @used-by not_like() + * @used-by or_not_like() + * + * @param mixed $field + * @param string $match + * @param string $type + * @param string $side + * @param string $not + * @param bool $escape + * @return CI_DB_query_builder + */ + protected function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '', $escape = NULL) + { + if ( ! is_array($field)) + { + $field = array($field => $match); + } + + is_bool($escape) OR $escape = $this->_protect_identifiers; + // lowercase $side in case somebody writes e.g. 'BEFORE' instead of 'before' (doh) + $side = strtolower($side); + + foreach ($field as $k => $v) + { + $prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) + ? $this->_group_get_type('') : $this->_group_get_type($type); + + if ($escape === TRUE) + { + $v = $this->escape_like_str($v); + } + + if ($side === 'none') + { + $like_statement = "{$prefix} {$k} {$not} LIKE '{$v}'"; + } + elseif ($side === 'before') + { + $like_statement = "{$prefix} {$k} {$not} LIKE '%{$v}'"; + } + elseif ($side === 'after') + { + $like_statement = "{$prefix} {$k} {$not} LIKE '{$v}%'"; + } + else + { + $like_statement = "{$prefix} {$k} {$not} LIKE '%{$v}%'"; + } + + // some platforms require an escape sequence definition for LIKE wildcards + if ($escape === TRUE && $this->_like_escape_str !== '') + { + $like_statement .= sprintf($this->_like_escape_str, $this->_like_escape_chr); + } + + $this->qb_where[] = array('condition' => $like_statement, 'escape' => $escape); + if ($this->qb_caching === TRUE) + { + $this->qb_cache_where[] = array('condition' => $like_statement, 'escape' => $escape); + $this->qb_cache_exists[] = 'where'; + } + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Starts a query group. + * + * @param string $not (Internal use only) + * @param string $type (Internal use only) + * @return CI_DB_query_builder + */ + public function group_start($not = '', $type = 'AND ') + { + $type = $this->_group_get_type($type); + + $this->qb_where_group_started = TRUE; + $prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) ? '' : $type; + $where = array( + 'condition' => $prefix.$not.str_repeat(' ', ++$this->qb_where_group_count).' (', + 'escape' => FALSE + ); + + $this->qb_where[] = $where; + if ($this->qb_caching) + { + $this->qb_cache_where[] = $where; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Starts a query group, but ORs the group + * + * @return CI_DB_query_builder + */ + public function or_group_start() + { + return $this->group_start('', 'OR '); + } + + // -------------------------------------------------------------------- + + /** + * Starts a query group, but NOTs the group + * + * @return CI_DB_query_builder + */ + public function not_group_start() + { + return $this->group_start('NOT ', 'AND '); + } + + // -------------------------------------------------------------------- + + /** + * Starts a query group, but OR NOTs the group + * + * @return CI_DB_query_builder + */ + public function or_not_group_start() + { + return $this->group_start('NOT ', 'OR '); + } + + // -------------------------------------------------------------------- + + /** + * Ends a query group + * + * @return CI_DB_query_builder + */ + public function group_end() + { + $this->qb_where_group_started = FALSE; + $where = array( + 'condition' => str_repeat(' ', $this->qb_where_group_count--).')', + 'escape' => FALSE + ); + + $this->qb_where[] = $where; + if ($this->qb_caching) + { + $this->qb_cache_where[] = $where; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Group_get_type + * + * @used-by group_start() + * @used-by _like() + * @used-by _wh() + * @used-by _where_in() + * + * @param string $type + * @return string + */ + protected function _group_get_type($type) + { + if ($this->qb_where_group_started) + { + $type = ''; + $this->qb_where_group_started = FALSE; + } + + return $type; + } + + // -------------------------------------------------------------------- + + /** + * GROUP BY + * + * @param string $by + * @param bool $escape + * @return CI_DB_query_builder + */ + public function group_by($by, $escape = NULL) + { + is_bool($escape) OR $escape = $this->_protect_identifiers; + + if (is_string($by)) + { + $by = ($escape === TRUE) + ? explode(',', $by) + : array($by); + } + + foreach ($by as $val) + { + $val = trim($val); + + if ($val !== '') + { + $val = array('field' => $val, 'escape' => $escape); + + $this->qb_groupby[] = $val; + if ($this->qb_caching === TRUE) + { + $this->qb_cache_groupby[] = $val; + $this->qb_cache_exists[] = 'groupby'; + } + } + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * HAVING + * + * Separates multiple calls with 'AND'. + * + * @param string $key + * @param string $value + * @param bool $escape + * @return object + */ + public function having($key, $value = NULL, $escape = NULL) + { + return $this->_wh('qb_having', $key, $value, 'AND ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * OR HAVING + * + * Separates multiple calls with 'OR'. + * + * @param string $key + * @param string $value + * @param bool $escape + * @return object + */ + public function or_having($key, $value = NULL, $escape = NULL) + { + return $this->_wh('qb_having', $key, $value, 'OR ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * ORDER BY + * + * @param string $orderby + * @param string $direction ASC, DESC or RANDOM + * @param bool $escape + * @return CI_DB_query_builder + */ + public function order_by($orderby, $direction = '', $escape = NULL) + { + $direction = strtoupper(trim($direction)); + + if ($direction === 'RANDOM') + { + $direction = ''; + + // Do we have a seed value? + $orderby = ctype_digit((string) $orderby) + ? sprintf($this->_random_keyword[1], $orderby) + : $this->_random_keyword[0]; + } + elseif (empty($orderby)) + { + return $this; + } + elseif ($direction !== '') + { + $direction = in_array($direction, array('ASC', 'DESC'), TRUE) ? ' '.$direction : ''; + } + + is_bool($escape) OR $escape = $this->_protect_identifiers; + + if ($escape === FALSE) + { + $qb_orderby[] = array('field' => $orderby, 'direction' => $direction, 'escape' => FALSE); + } + else + { + $qb_orderby = array(); + foreach (explode(',', $orderby) as $field) + { + $qb_orderby[] = ($direction === '' && preg_match('/\s+(ASC|DESC)$/i', rtrim($field), $match, PREG_OFFSET_CAPTURE)) + ? array('field' => ltrim(substr($field, 0, $match[0][1])), 'direction' => ' '.$match[1][0], 'escape' => TRUE) + : array('field' => trim($field), 'direction' => $direction, 'escape' => TRUE); + } + } + + $this->qb_orderby = array_merge($this->qb_orderby, $qb_orderby); + if ($this->qb_caching === TRUE) + { + $this->qb_cache_orderby = array_merge($this->qb_cache_orderby, $qb_orderby); + $this->qb_cache_exists[] = 'orderby'; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * LIMIT + * + * @param int $value LIMIT value + * @param int $offset OFFSET value + * @return CI_DB_query_builder + */ + public function limit($value, $offset = 0) + { + is_null($value) OR $this->qb_limit = (int) $value; + empty($offset) OR $this->qb_offset = (int) $offset; + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Sets the OFFSET value + * + * @param int $offset OFFSET value + * @return CI_DB_query_builder + */ + public function offset($offset) + { + empty($offset) OR $this->qb_offset = (int) $offset; + return $this; + } + + // -------------------------------------------------------------------- + + /** + * LIMIT string + * + * Generates a platform-specific LIMIT clause. + * + * @param string $sql SQL Query + * @return string + */ + protected function _limit($sql) + { + return $sql.' LIMIT '.($this->qb_offset ? $this->qb_offset.', ' : '').$this->qb_limit; + } + + // -------------------------------------------------------------------- + + /** + * The "set" function. + * + * Allows key/value pairs to be set for inserting or updating + * + * @param mixed + * @param string + * @param bool + * @return CI_DB_query_builder + */ + public function set($key, $value = '', $escape = NULL) + { + $key = $this->_object_to_array($key); + + if ( ! is_array($key)) + { + $key = array($key => $value); + } + + is_bool($escape) OR $escape = $this->_protect_identifiers; + + foreach ($key as $k => $v) + { + $this->qb_set[$this->protect_identifiers($k, FALSE, $escape)] = ($escape) + ? $this->escape($v) : $v; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Get SELECT query string + * + * Compiles a SELECT query string and returns the sql. + * + * @param string the table name to select from (optional) + * @param bool TRUE: resets QB values; FALSE: leave QB values alone + * @return string + */ + public function get_compiled_select($table = '', $reset = TRUE) + { + if ($table !== '') + { + $this->_track_aliases($table); + $this->from($table); + } + + $select = $this->_compile_select(); + + if ($reset === TRUE) + { + $this->_reset_select(); + } + + return $select; + } + + // -------------------------------------------------------------------- + + /** + * Get + * + * Compiles the select statement based on the other functions called + * and runs the query + * + * @param string the table + * @param string the limit clause + * @param string the offset clause + * @return object + */ + public function get($table = '', $limit = NULL, $offset = NULL) + { + if ($table !== '') + { + $this->_track_aliases($table); + $this->from($table); + } + + if ( ! empty($limit)) + { + $this->limit($limit, $offset); + } + + $result = $this->query($this->_compile_select()); + $this->_reset_select(); + return $result; + } + + // -------------------------------------------------------------------- + + /** + * "Count All Results" query + * + * Generates a platform-specific query string that counts all records + * returned by an Query Builder query. + * + * @param string + * @param bool the reset clause + * @return int + */ + public function count_all_results($table = '', $reset = TRUE) + { + if ($table !== '') + { + $this->_track_aliases($table); + $this->from($table); + } + + $result = ($this->qb_distinct === TRUE) + ? $this->query($this->_count_string.$this->protect_identifiers('numrows')."\nFROM (\n".$this->_compile_select()."\n) CI_count_all_results") + : $this->query($this->_compile_select($this->_count_string.$this->protect_identifiers('numrows'))); + + if ($reset === TRUE) + { + $this->_reset_select(); + } + + if ($result->num_rows() === 0) + { + return 0; + } + + $row = $result->row(); + return (int) $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * Get_Where + * + * Allows the where clause, limit and offset to be added directly + * + * @param string $table + * @param string $where + * @param int $limit + * @param int $offset + * @return object + */ + public function get_where($table = '', $where = NULL, $limit = NULL, $offset = NULL) + { + if ($table !== '') + { + $this->from($table); + } + + if ($where !== NULL) + { + $this->where($where); + } + + if ( ! empty($limit)) + { + $this->limit($limit, $offset); + } + + $result = $this->query($this->_compile_select()); + $this->_reset_select(); + return $result; + } + + // -------------------------------------------------------------------- + + /** + * Insert_Batch + * + * Compiles batch insert strings and runs the queries + * + * @param string $table Table to insert into + * @param array $set An associative array of insert values + * @param bool $escape Whether to escape values and identifiers + * @return int Number of rows inserted or FALSE on failure + */ + public function insert_batch($table = '', $set = NULL, $escape = NULL) + { + if ($set !== NULL) + { + $this->set_insert_batch($set, '', $escape); + } + + if (count($this->qb_set) === 0) + { + // No valid data array. Folds in cases where keys and values did not match up + return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE; + } + + if ($table === '') + { + if ( ! isset($this->qb_from[0])) + { + return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE; + } + + $table = $this->qb_from[0]; + } + + // Batch this baby + $affected_rows = 0; + for ($i = 0, $total = count($this->qb_set); $i < $total; $i += 100) + { + $this->query($this->_insert_batch($this->protect_identifiers($table, TRUE, $escape, FALSE), $this->qb_keys, array_slice($this->qb_set, $i, 100))); + $affected_rows += $this->affected_rows(); + } + + $this->_reset_write(); + return $affected_rows; + } + + // -------------------------------------------------------------------- + + /** + * Insert batch statement + * + * Generates a platform-specific insert string from the supplied data. + * + * @param string $table Table name + * @param array $keys INSERT keys + * @param array $values INSERT values + * @return string + */ + protected function _insert_batch($table, $keys, $values) + { + return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values); + } + + // -------------------------------------------------------------------- + + /** + * The "set_insert_batch" function. Allows key/value pairs to be set for batch inserts + * + * @param mixed + * @param string + * @param bool + * @return CI_DB_query_builder + */ + public function set_insert_batch($key, $value = '', $escape = NULL) + { + $key = $this->_object_to_array_batch($key); + + if ( ! is_array($key)) + { + $key = array($key => $value); + } + + is_bool($escape) OR $escape = $this->_protect_identifiers; + + $keys = array_keys($this->_object_to_array(current($key))); + sort($keys); + + foreach ($key as $row) + { + $row = $this->_object_to_array($row); + if (count(array_diff($keys, array_keys($row))) > 0 OR count(array_diff(array_keys($row), $keys)) > 0) + { + // batch function above returns an error on an empty array + $this->qb_set[] = array(); + return; + } + + ksort($row); // puts $row in the same order as our keys + + if ($escape !== FALSE) + { + $clean = array(); + foreach ($row as $value) + { + $clean[] = $this->escape($value); + } + + $row = $clean; + } + + $this->qb_set[] = '('.implode(',', $row).')'; + } + + foreach ($keys as $k) + { + $this->qb_keys[] = $this->protect_identifiers($k, FALSE, $escape); + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Get INSERT query string + * + * Compiles an insert query and returns the sql + * + * @param string the table to insert into + * @param bool TRUE: reset QB values; FALSE: leave QB values alone + * @return string + */ + public function get_compiled_insert($table = '', $reset = TRUE) + { + if ($this->_validate_insert($table) === FALSE) + { + return FALSE; + } + + $sql = $this->_insert( + $this->protect_identifiers( + $this->qb_from[0], TRUE, NULL, FALSE + ), + array_keys($this->qb_set), + array_values($this->qb_set) + ); + + if ($reset === TRUE) + { + $this->_reset_write(); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Insert + * + * Compiles an insert string and runs the query + * + * @param string the table to insert data into + * @param array an associative array of insert values + * @param bool $escape Whether to escape values and identifiers + * @return object + */ + public function insert($table = '', $set = NULL, $escape = NULL) + { + if ($set !== NULL) + { + $this->set($set, '', $escape); + } + + if ($this->_validate_insert($table) === FALSE) + { + return FALSE; + } + + $sql = $this->_insert( + $this->protect_identifiers( + $this->qb_from[0], TRUE, $escape, FALSE + ), + array_keys($this->qb_set), + array_values($this->qb_set) + ); + + $this->_reset_write(); + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Validate Insert + * + * This method is used by both insert() and get_compiled_insert() to + * validate that the there data is actually being set and that table + * has been chosen to be inserted into. + * + * @param string the table to insert data into + * @return string + */ + protected function _validate_insert($table = '') + { + if (count($this->qb_set) === 0) + { + return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE; + } + + if ($table !== '') + { + $this->qb_from[0] = $table; + } + elseif ( ! isset($this->qb_from[0])) + { + return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Replace + * + * Compiles an replace into string and runs the query + * + * @param string the table to replace data into + * @param array an associative array of insert values + * @return object + */ + public function replace($table = '', $set = NULL) + { + if ($set !== NULL) + { + $this->set($set); + } + + if (count($this->qb_set) === 0) + { + return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE; + } + + if ($table === '') + { + if ( ! isset($this->qb_from[0])) + { + return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE; + } + + $table = $this->qb_from[0]; + } + + $sql = $this->_replace($this->protect_identifiers($table, TRUE, NULL, FALSE), array_keys($this->qb_set), array_values($this->qb_set)); + + $this->_reset_write(); + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Replace statement + * + * Generates a platform-specific replace string from the supplied data + * + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + protected function _replace($table, $keys, $values) + { + return 'REPLACE INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')'; + } + + // -------------------------------------------------------------------- + + /** + * FROM tables + * + * Groups tables in FROM clauses if needed, so there is no confusion + * about operator precedence. + * + * Note: This is only used (and overridden) by MySQL and CUBRID. + * + * @return string + */ + protected function _from_tables() + { + return implode(', ', $this->qb_from); + } + + // -------------------------------------------------------------------- + + /** + * Get UPDATE query string + * + * Compiles an update query and returns the sql + * + * @param string the table to update + * @param bool TRUE: reset QB values; FALSE: leave QB values alone + * @return string + */ + public function get_compiled_update($table = '', $reset = TRUE) + { + // Combine any cached components with the current statements + $this->_merge_cache(); + + if ($this->_validate_update($table) === FALSE) + { + return FALSE; + } + + $sql = $this->_update($this->qb_from[0], $this->qb_set); + + if ($reset === TRUE) + { + $this->_reset_write(); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * UPDATE + * + * Compiles an update string and runs the query. + * + * @param string $table + * @param array $set An associative array of update values + * @param mixed $where + * @param int $limit + * @return object + */ + public function update($table = '', $set = NULL, $where = NULL, $limit = NULL) + { + // Combine any cached components with the current statements + $this->_merge_cache(); + + if ($set !== NULL) + { + $this->set($set); + } + + if ($this->_validate_update($table) === FALSE) + { + return FALSE; + } + + if ($where !== NULL) + { + $this->where($where); + } + + if ( ! empty($limit)) + { + $this->limit($limit); + } + + $sql = $this->_update($this->qb_from[0], $this->qb_set); + $this->_reset_write(); + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Validate Update + * + * This method is used by both update() and get_compiled_update() to + * validate that data is actually being set and that a table has been + * chosen to be update. + * + * @param string the table to update data on + * @return bool + */ + protected function _validate_update($table) + { + if (count($this->qb_set) === 0) + { + return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE; + } + + if ($table !== '') + { + $this->qb_from = array($this->protect_identifiers($table, TRUE, NULL, FALSE)); + } + elseif ( ! isset($this->qb_from[0])) + { + return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Update_Batch + * + * Compiles an update string and runs the query + * + * @param string the table to retrieve the results from + * @param array an associative array of update values + * @param string the where key + * @return int number of rows affected or FALSE on failure + */ + public function update_batch($table = '', $set = NULL, $index = NULL) + { + // Combine any cached components with the current statements + $this->_merge_cache(); + + if ($index === NULL) + { + return ($this->db_debug) ? $this->display_error('db_must_use_index') : FALSE; + } + + if ($set !== NULL) + { + $this->set_update_batch($set, $index); + } + + if (count($this->qb_set) === 0) + { + return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE; + } + + if ($table === '') + { + if ( ! isset($this->qb_from[0])) + { + return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE; + } + + $table = $this->qb_from[0]; + } + + // Batch this baby + $affected_rows = 0; + for ($i = 0, $total = count($this->qb_set); $i < $total; $i += 100) + { + $this->query($this->_update_batch($this->protect_identifiers($table, TRUE, NULL, FALSE), array_slice($this->qb_set, $i, 100), $this->protect_identifiers($index))); + $affected_rows += $this->affected_rows(); + $this->qb_where = array(); + } + + $this->_reset_write(); + return $affected_rows; + } + + // -------------------------------------------------------------------- + + /** + * Update_Batch statement + * + * Generates a platform-specific batch update string from the supplied data + * + * @param string $table Table name + * @param array $values Update data + * @param string $index WHERE key + * @return string + */ + protected function _update_batch($table, $values, $index) + { + $ids = array(); + foreach ($values as $key => $val) + { + $ids[] = $val[$index]; + + foreach (array_keys($val) as $field) + { + if ($field !== $index) + { + $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field]; + } + } + } + + $cases = ''; + foreach ($final as $k => $v) + { + $cases .= $k." = CASE \n" + .implode("\n", $v)."\n" + .'ELSE '.$k.' END, '; + } + + $this->where($index.' IN('.implode(',', $ids).')', NULL, FALSE); + + return 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where'); + } + + // -------------------------------------------------------------------- + + /** + * The "set_update_batch" function. Allows key/value pairs to be set for batch updating + * + * @param array + * @param string + * @param bool + * @return CI_DB_query_builder + */ + public function set_update_batch($key, $index = '', $escape = NULL) + { + $key = $this->_object_to_array_batch($key); + + if ( ! is_array($key)) + { + // @todo error + } + + is_bool($escape) OR $escape = $this->_protect_identifiers; + + foreach ($key as $k => $v) + { + $index_set = FALSE; + $clean = array(); + foreach ($v as $k2 => $v2) + { + if ($k2 === $index) + { + $index_set = TRUE; + } + + $clean[$this->protect_identifiers($k2, FALSE, $escape)] = ($escape === FALSE) ? $v2 : $this->escape($v2); + } + + if ($index_set === FALSE) + { + return $this->display_error('db_batch_missing_index'); + } + + $this->qb_set[] = $clean; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Empty Table + * + * Compiles a delete string and runs "DELETE FROM table" + * + * @param string the table to empty + * @return object + */ + public function empty_table($table = '') + { + if ($table === '') + { + if ( ! isset($this->qb_from[0])) + { + return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE; + } + + $table = $this->qb_from[0]; + } + else + { + $table = $this->protect_identifiers($table, TRUE, NULL, FALSE); + } + + $sql = $this->_delete($table); + $this->_reset_write(); + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Truncate + * + * Compiles a truncate string and runs the query + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @param string the table to truncate + * @return object + */ + public function truncate($table = '') + { + if ($table === '') + { + if ( ! isset($this->qb_from[0])) + { + return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE; + } + + $table = $this->qb_from[0]; + } + else + { + $table = $this->protect_identifiers($table, TRUE, NULL, FALSE); + } + + $sql = $this->_truncate($table); + $this->_reset_write(); + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * + * If the database does not support the truncate() command, + * then this method maps to 'DELETE FROM table' + * + * @param string the table name + * @return string + */ + protected function _truncate($table) + { + return 'TRUNCATE '.$table; + } + + // -------------------------------------------------------------------- + + /** + * Get DELETE query string + * + * Compiles a delete query string and returns the sql + * + * @param string the table to delete from + * @param bool TRUE: reset QB values; FALSE: leave QB values alone + * @return string + */ + public function get_compiled_delete($table = '', $reset = TRUE) + { + $this->return_delete_sql = TRUE; + $sql = $this->delete($table, '', NULL, $reset); + $this->return_delete_sql = FALSE; + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Delete + * + * Compiles a delete string and runs the query + * + * @param mixed the table(s) to delete from. String or array + * @param mixed the where clause + * @param mixed the limit clause + * @param bool + * @return mixed + */ + public function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE) + { + // Combine any cached components with the current statements + $this->_merge_cache(); + + if ($table === '') + { + if ( ! isset($this->qb_from[0])) + { + return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE; + } + + $table = $this->qb_from[0]; + } + elseif (is_array($table)) + { + empty($where) && $reset_data = FALSE; + + foreach ($table as $single_table) + { + $this->delete($single_table, $where, $limit, $reset_data); + } + + return; + } + else + { + $table = $this->protect_identifiers($table, TRUE, NULL, FALSE); + } + + if ($where !== '') + { + $this->where($where); + } + + if ( ! empty($limit)) + { + $this->limit($limit); + } + + if (count($this->qb_where) === 0) + { + return ($this->db_debug) ? $this->display_error('db_del_must_use_where') : FALSE; + } + + $sql = $this->_delete($table); + if ($reset_data) + { + $this->_reset_write(); + } + + return ($this->return_delete_sql === TRUE) ? $sql : $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @param string the table name + * @return string + */ + protected function _delete($table) + { + return 'DELETE FROM '.$table.$this->_compile_wh('qb_where') + .($this->qb_limit ? ' LIMIT '.$this->qb_limit : ''); + } + + // -------------------------------------------------------------------- + + /** + * DB Prefix + * + * Prepends a database prefix if one exists in configuration + * + * @param string the table + * @return string + */ + public function dbprefix($table = '') + { + if ($table === '') + { + $this->display_error('db_table_name_required'); + } + + return $this->dbprefix.$table; + } + + // -------------------------------------------------------------------- + + /** + * Set DB Prefix + * + * Set's the DB Prefix to something new without needing to reconnect + * + * @param string the prefix + * @return string + */ + public function set_dbprefix($prefix = '') + { + return $this->dbprefix = $prefix; + } + + // -------------------------------------------------------------------- + + /** + * Track Aliases + * + * Used to track SQL statements written with aliased tables. + * + * @param string The table to inspect + * @return string + */ + protected function _track_aliases($table) + { + if (is_array($table)) + { + foreach ($table as $t) + { + $this->_track_aliases($t); + } + return; + } + + // Does the string contain a comma? If so, we need to separate + // the string into discreet statements + if (strpos($table, ',') !== FALSE) + { + return $this->_track_aliases(explode(',', $table)); + } + + // if a table alias is used we can recognize it by a space + if (strpos($table, ' ') !== FALSE) + { + // if the alias is written with the AS keyword, remove it + $table = preg_replace('/\s+AS\s+/i', ' ', $table); + + // Grab the alias + $table = trim(strrchr($table, ' ')); + + // Store the alias, if it doesn't already exist + if ( ! in_array($table, $this->qb_aliased_tables)) + { + $this->qb_aliased_tables[] = $table; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Compile the SELECT statement + * + * Generates a query string based on which functions were used. + * Should not be called directly. + * + * @param bool $select_override + * @return string + */ + protected function _compile_select($select_override = FALSE) + { + // Combine any cached components with the current statements + $this->_merge_cache(); + + // Write the "select" portion of the query + if ($select_override !== FALSE) + { + $sql = $select_override; + } + else + { + $sql = ( ! $this->qb_distinct) ? 'SELECT ' : 'SELECT DISTINCT '; + + if (count($this->qb_select) === 0) + { + $sql .= '*'; + } + else + { + // Cycle through the "select" portion of the query and prep each column name. + // The reason we protect identifiers here rather than in the select() function + // is because until the user calls the from() function we don't know if there are aliases + foreach ($this->qb_select as $key => $val) + { + $no_escape = isset($this->qb_no_escape[$key]) ? $this->qb_no_escape[$key] : NULL; + $this->qb_select[$key] = $this->protect_identifiers($val, FALSE, $no_escape); + } + + $sql .= implode(', ', $this->qb_select); + } + } + + // Write the "FROM" portion of the query + if (count($this->qb_from) > 0) + { + $sql .= "\nFROM ".$this->_from_tables(); + } + + // Write the "JOIN" portion of the query + if (count($this->qb_join) > 0) + { + $sql .= "\n".implode("\n", $this->qb_join); + } + + $sql .= $this->_compile_wh('qb_where') + .$this->_compile_group_by() + .$this->_compile_wh('qb_having') + .$this->_compile_order_by(); // ORDER BY + + // LIMIT + if ($this->qb_limit) + { + return $this->_limit($sql."\n"); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Compile WHERE, HAVING statements + * + * Escapes identifiers in WHERE and HAVING statements at execution time. + * + * Required so that aliases are tracked properly, regardless of wether + * where(), or_where(), having(), or_having are called prior to from(), + * join() and dbprefix is added only if needed. + * + * @param string $qb_key 'qb_where' or 'qb_having' + * @return string SQL statement + */ + protected function _compile_wh($qb_key) + { + if (count($this->$qb_key) > 0) + { + for ($i = 0, $c = count($this->$qb_key); $i < $c; $i++) + { + // Is this condition already compiled? + if (is_string($this->{$qb_key}[$i])) + { + continue; + } + elseif ($this->{$qb_key}[$i]['escape'] === FALSE) + { + $this->{$qb_key}[$i] = $this->{$qb_key}[$i]['condition']; + continue; + } + + // Split multiple conditions + $conditions = preg_split( + '/((?:^|\s+)AND\s+|(?:^|\s+)OR\s+)/i', + $this->{$qb_key}[$i]['condition'], + -1, + PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY + ); + + for ($ci = 0, $cc = count($conditions); $ci < $cc; $ci++) + { + if (($op = $this->_get_operator($conditions[$ci])) === FALSE + OR ! preg_match('/^(\(?)(.*)('.preg_quote($op, '/').')\s*(.*(? '(test <= foo)', /* the whole thing */ + // 1 => '(', /* optional */ + // 2 => 'test', /* the field name */ + // 3 => ' <= ', /* $op */ + // 4 => 'foo', /* optional, if $op is e.g. 'IS NULL' */ + // 5 => ')' /* optional */ + // ); + + if ( ! empty($matches[4])) + { + $this->_is_literal($matches[4]) OR $matches[4] = $this->protect_identifiers(trim($matches[4])); + $matches[4] = ' '.$matches[4]; + } + + $conditions[$ci] = $matches[1].$this->protect_identifiers(trim($matches[2])) + .' '.trim($matches[3]).$matches[4].$matches[5]; + } + + $this->{$qb_key}[$i] = implode('', $conditions); + } + + return ($qb_key === 'qb_having' ? "\nHAVING " : "\nWHERE ") + .implode("\n", $this->$qb_key); + } + + return ''; + } + + // -------------------------------------------------------------------- + + /** + * Compile GROUP BY + * + * Escapes identifiers in GROUP BY statements at execution time. + * + * Required so that aliases are tracked properly, regardless of wether + * group_by() is called prior to from(), join() and dbprefix is added + * only if needed. + * + * @return string SQL statement + */ + protected function _compile_group_by() + { + if (count($this->qb_groupby) > 0) + { + for ($i = 0, $c = count($this->qb_groupby); $i < $c; $i++) + { + // Is it already compiled? + if (is_string($this->qb_groupby[$i])) + { + continue; + } + + $this->qb_groupby[$i] = ($this->qb_groupby[$i]['escape'] === FALSE OR $this->_is_literal($this->qb_groupby[$i]['field'])) + ? $this->qb_groupby[$i]['field'] + : $this->protect_identifiers($this->qb_groupby[$i]['field']); + } + + return "\nGROUP BY ".implode(', ', $this->qb_groupby); + } + + return ''; + } + + // -------------------------------------------------------------------- + + /** + * Compile ORDER BY + * + * Escapes identifiers in ORDER BY statements at execution time. + * + * Required so that aliases are tracked properly, regardless of wether + * order_by() is called prior to from(), join() and dbprefix is added + * only if needed. + * + * @return string SQL statement + */ + protected function _compile_order_by() + { + if (is_array($this->qb_orderby) && count($this->qb_orderby) > 0) + { + for ($i = 0, $c = count($this->qb_orderby); $i < $c; $i++) + { + if ($this->qb_orderby[$i]['escape'] !== FALSE && ! $this->_is_literal($this->qb_orderby[$i]['field'])) + { + $this->qb_orderby[$i]['field'] = $this->protect_identifiers($this->qb_orderby[$i]['field']); + } + + $this->qb_orderby[$i] = $this->qb_orderby[$i]['field'].$this->qb_orderby[$i]['direction']; + } + + return $this->qb_orderby = "\nORDER BY ".implode(', ', $this->qb_orderby); + } + elseif (is_string($this->qb_orderby)) + { + return $this->qb_orderby; + } + + return ''; + } + + // -------------------------------------------------------------------- + + /** + * Object to Array + * + * Takes an object as input and converts the class variables to array key/vals + * + * @param object + * @return array + */ + protected function _object_to_array($object) + { + if ( ! is_object($object)) + { + return $object; + } + + $array = array(); + foreach (get_object_vars($object) as $key => $val) + { + // There are some built in keys we need to ignore for this conversion + if ( ! is_object($val) && ! is_array($val) && $key !== '_parent_name') + { + $array[$key] = $val; + } + } + + return $array; + } + + // -------------------------------------------------------------------- + + /** + * Object to Array + * + * Takes an object as input and converts the class variables to array key/vals + * + * @param object + * @return array + */ + protected function _object_to_array_batch($object) + { + if ( ! is_object($object)) + { + return $object; + } + + $array = array(); + $out = get_object_vars($object); + $fields = array_keys($out); + + foreach ($fields as $val) + { + // There are some built in keys we need to ignore for this conversion + if ($val !== '_parent_name') + { + $i = 0; + foreach ($out[$val] as $data) + { + $array[$i++][$val] = $data; + } + } + } + + return $array; + } + + // -------------------------------------------------------------------- + + /** + * Start Cache + * + * Starts QB caching + * + * @return CI_DB_query_builder + */ + public function start_cache() + { + $this->qb_caching = TRUE; + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Stop Cache + * + * Stops QB caching + * + * @return CI_DB_query_builder + */ + public function stop_cache() + { + $this->qb_caching = FALSE; + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Flush Cache + * + * Empties the QB cache + * + * @return CI_DB_query_builder + */ + public function flush_cache() + { + $this->_reset_run(array( + 'qb_cache_select' => array(), + 'qb_cache_from' => array(), + 'qb_cache_join' => array(), + 'qb_cache_where' => array(), + 'qb_cache_groupby' => array(), + 'qb_cache_having' => array(), + 'qb_cache_orderby' => array(), + 'qb_cache_set' => array(), + 'qb_cache_exists' => array(), + 'qb_cache_no_escape' => array() + )); + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Merge Cache + * + * When called, this function merges any cached QB arrays with + * locally called ones. + * + * @return void + */ + protected function _merge_cache() + { + if (count($this->qb_cache_exists) === 0) + { + return; + } + elseif (in_array('select', $this->qb_cache_exists, TRUE)) + { + $qb_no_escape = $this->qb_cache_no_escape; + } + + foreach (array_unique($this->qb_cache_exists) as $val) // select, from, etc. + { + $qb_variable = 'qb_'.$val; + $qb_cache_var = 'qb_cache_'.$val; + $qb_new = $this->$qb_cache_var; + + for ($i = 0, $c = count($this->$qb_variable); $i < $c; $i++) + { + if ( ! in_array($this->{$qb_variable}[$i], $qb_new, TRUE)) + { + $qb_new[] = $this->{$qb_variable}[$i]; + if ($val === 'select') + { + $qb_no_escape[] = $this->qb_no_escape[$i]; + } + } + } + + $this->$qb_variable = $qb_new; + if ($val === 'select') + { + $this->qb_no_escape = $qb_no_escape; + } + } + + // If we are "protecting identifiers" we need to examine the "from" + // portion of the query to determine if there are any aliases + if ($this->_protect_identifiers === TRUE && count($this->qb_cache_from) > 0) + { + $this->_track_aliases($this->qb_from); + } + } + + // -------------------------------------------------------------------- + + /** + * Is literal + * + * Determines if a string represents a literal value or a field name + * + * @param string $str + * @return bool + */ + protected function _is_literal($str) + { + $str = trim($str); + + if (empty($str) OR ctype_digit($str) OR (string) (float) $str === $str OR in_array(strtoupper($str), array('TRUE', 'FALSE'), TRUE)) + { + return TRUE; + } + + static $_str; + + if (empty($_str)) + { + $_str = ($this->_escape_char !== '"') + ? array('"', "'") : array("'"); + } + + return in_array($str[0], $_str, TRUE); + } + + // -------------------------------------------------------------------- + + /** + * Reset Query Builder values. + * + * Publicly-visible method to reset the QB values. + * + * @return CI_DB_query_builder + */ + public function reset_query() + { + $this->_reset_select(); + $this->_reset_write(); + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Resets the query builder values. Called by the get() function + * + * @param array An array of fields to reset + * @return void + */ + protected function _reset_run($qb_reset_items) + { + foreach ($qb_reset_items as $item => $default_value) + { + $this->$item = $default_value; + } + } + + // -------------------------------------------------------------------- + + /** + * Resets the query builder values. Called by the get() function + * + * @return void + */ + protected function _reset_select() + { + $this->_reset_run(array( + 'qb_select' => array(), + 'qb_from' => array(), + 'qb_join' => array(), + 'qb_where' => array(), + 'qb_groupby' => array(), + 'qb_having' => array(), + 'qb_orderby' => array(), + 'qb_aliased_tables' => array(), + 'qb_no_escape' => array(), + 'qb_distinct' => FALSE, + 'qb_limit' => FALSE, + 'qb_offset' => FALSE + )); + } + + // -------------------------------------------------------------------- + + /** + * Resets the query builder "write" values. + * + * Called by the insert() update() insert_batch() update_batch() and delete() functions + * + * @return void + */ + protected function _reset_write() + { + $this->_reset_run(array( + 'qb_set' => array(), + 'qb_from' => array(), + 'qb_join' => array(), + 'qb_where' => array(), + 'qb_orderby' => array(), + 'qb_keys' => array(), + 'qb_limit' => FALSE + )); + } + +} diff --git a/codeigniter-3.0/system/database/DB_result.php b/ci-3.0/system/database/DB_result.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/DB_result.php rename to ci-3.0/system/database/DB_result.php diff --git a/ci-3.0/system/database/DB_utility.php b/ci-3.0/system/database/DB_utility.php new file mode 100755 index 000000000..b51893e18 --- /dev/null +++ b/ci-3.0/system/database/DB_utility.php @@ -0,0 +1,424 @@ +db =& $db; + log_message('info', 'Database Utility Class Initialized'); + } + + // -------------------------------------------------------------------- + + /** + * List databases + * + * @return array + */ + public function list_databases() + { + // Is there a cached result? + if (isset($this->db->data_cache['db_names'])) + { + return $this->db->data_cache['db_names']; + } + elseif ($this->_list_databases === FALSE) + { + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + + $this->db->data_cache['db_names'] = array(); + + $query = $this->db->query($this->_list_databases); + if ($query === FALSE) + { + return $this->db->data_cache['db_names']; + } + + for ($i = 0, $query = $query->result_array(), $c = count($query); $i < $c; $i++) + { + $this->db->data_cache['db_names'][] = current($query[$i]); + } + + return $this->db->data_cache['db_names']; + } + + // -------------------------------------------------------------------- + + /** + * Determine if a particular database exists + * + * @param string $database_name + * @return bool + */ + public function database_exists($database_name) + { + return in_array($database_name, $this->list_databases()); + } + + // -------------------------------------------------------------------- + + /** + * Optimize Table + * + * @param string $table_name + * @return mixed + */ + public function optimize_table($table_name) + { + if ($this->_optimize_table === FALSE) + { + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + + $query = $this->db->query(sprintf($this->_optimize_table, $this->db->escape_identifiers($table_name))); + if ($query !== FALSE) + { + $query = $query->result_array(); + return current($query); + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Optimize Database + * + * @return mixed + */ + public function optimize_database() + { + if ($this->_optimize_table === FALSE) + { + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + + $result = array(); + foreach ($this->db->list_tables() as $table_name) + { + $res = $this->db->query(sprintf($this->_optimize_table, $this->db->escape_identifiers($table_name))); + if (is_bool($res)) + { + return $res; + } + + // Build the result array... + $res = $res->result_array(); + $res = current($res); + $key = str_replace($this->db->database.'.', '', current($res)); + $keys = array_keys($res); + unset($res[$keys[0]]); + + $result[$key] = $res; + } + + return $result; + } + + // -------------------------------------------------------------------- + + /** + * Repair Table + * + * @param string $table_name + * @return mixed + */ + public function repair_table($table_name) + { + if ($this->_repair_table === FALSE) + { + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + + $query = $this->db->query(sprintf($this->_repair_table, $this->db->escape_identifiers($table_name))); + if (is_bool($query)) + { + return $query; + } + + $query = $query->result_array(); + return current($query); + } + + // -------------------------------------------------------------------- + + /** + * Generate CSV from a query result object + * + * @param object $query Query result object + * @param string $delim Delimiter (default: ,) + * @param string $newline Newline character (default: \n) + * @param string $enclosure Enclosure (default: ") + * @return string + */ + public function csv_from_result($query, $delim = ',', $newline = "\n", $enclosure = '"') + { + if ( ! is_object($query) OR ! method_exists($query, 'list_fields')) + { + show_error('You must submit a valid result object'); + } + + $out = ''; + // First generate the headings from the table column names + foreach ($query->list_fields() as $name) + { + $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $name).$enclosure.$delim; + } + + $out = substr($out, 0, -strlen($delim)).$newline; + + // Next blast through the result array and build out the rows + while ($row = $query->unbuffered_row('array')) + { + $line = array(); + foreach ($row as $item) + { + $line[] = $enclosure.str_replace($enclosure, $enclosure.$enclosure, $item).$enclosure; + } + $out .= implode($delim, $line).$newline; + } + + return $out; + } + + // -------------------------------------------------------------------- + + /** + * Generate XML data from a query result object + * + * @param object $query Query result object + * @param array $params Any preferences + * @return string + */ + public function xml_from_result($query, $params = array()) + { + if ( ! is_object($query) OR ! method_exists($query, 'list_fields')) + { + show_error('You must submit a valid result object'); + } + + // Set our default values + foreach (array('root' => 'root', 'element' => 'element', 'newline' => "\n", 'tab' => "\t") as $key => $val) + { + if ( ! isset($params[$key])) + { + $params[$key] = $val; + } + } + + // Create variables for convenience + extract($params); + + // Load the xml helper + get_instance()->load->helper('xml'); + + // Generate the result + $xml = '<'.$root.'>'.$newline; + while ($row = $query->unbuffered_row()) + { + $xml .= $tab.'<'.$element.'>'.$newline; + foreach ($row as $key => $val) + { + $xml .= $tab.$tab.'<'.$key.'>'.xml_convert($val).''.$newline; + } + $xml .= $tab.''.$newline; + } + + return $xml.''.$newline; + } + + // -------------------------------------------------------------------- + + /** + * Database Backup + * + * @param array $params + * @return string + */ + public function backup($params = array()) + { + // If the parameters have not been submitted as an + // array then we know that it is simply the table + // name, which is a valid short cut. + if (is_string($params)) + { + $params = array('tables' => $params); + } + + // Set up our default preferences + $prefs = array( + 'tables' => array(), + 'ignore' => array(), + 'filename' => '', + 'format' => 'gzip', // gzip, zip, txt + 'add_drop' => TRUE, + 'add_insert' => TRUE, + 'newline' => "\n", + 'foreign_key_checks' => TRUE + ); + + // Did the user submit any preferences? If so set them.... + if (count($params) > 0) + { + foreach ($prefs as $key => $val) + { + if (isset($params[$key])) + { + $prefs[$key] = $params[$key]; + } + } + } + + // Are we backing up a complete database or individual tables? + // If no table names were submitted we'll fetch the entire table list + if (count($prefs['tables']) === 0) + { + $prefs['tables'] = $this->db->list_tables(); + } + + // Validate the format + if ( ! in_array($prefs['format'], array('gzip', 'zip', 'txt'), TRUE)) + { + $prefs['format'] = 'txt'; + } + + // Is the encoder supported? If not, we'll either issue an + // error or use plain text depending on the debug settings + if (($prefs['format'] === 'gzip' && ! function_exists('gzencode')) + OR ($prefs['format'] === 'zip' && ! function_exists('gzcompress'))) + { + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsupported_compression'); + } + + $prefs['format'] = 'txt'; + } + + // Was a Zip file requested? + if ($prefs['format'] === 'zip') + { + // Set the filename if not provided (only needed with Zip files) + if ($prefs['filename'] === '') + { + $prefs['filename'] = (count($prefs['tables']) === 1 ? $prefs['tables'] : $this->db->database) + .date('Y-m-d_H-i', time()).'.sql'; + } + else + { + // If they included the .zip file extension we'll remove it + if (preg_match('|.+?\.zip$|', $prefs['filename'])) + { + $prefs['filename'] = str_replace('.zip', '', $prefs['filename']); + } + + // Tack on the ".sql" file extension if needed + if ( ! preg_match('|.+?\.sql$|', $prefs['filename'])) + { + $prefs['filename'] .= '.sql'; + } + } + + // Load the Zip class and output it + $CI =& get_instance(); + $CI->load->library('zip'); + $CI->zip->add_data($prefs['filename'], $this->_backup($prefs)); + return $CI->zip->get_zip(); + } + elseif ($prefs['format'] === 'txt') // Was a text file requested? + { + return $this->_backup($prefs); + } + elseif ($prefs['format'] === 'gzip') // Was a Gzip file requested? + { + return gzencode($this->_backup($prefs)); + } + + return; + } + +} diff --git a/ci-3.0/system/database/drivers/cubrid/cubrid_driver.php b/ci-3.0/system/database/drivers/cubrid/cubrid_driver.php new file mode 100755 index 000000000..65f4adb3f --- /dev/null +++ b/ci-3.0/system/database/drivers/cubrid/cubrid_driver.php @@ -0,0 +1,405 @@ +dsn, $matches)) + { + if (stripos($matches[2], 'autocommit=off') !== FALSE) + { + $this->auto_commit = FALSE; + } + } + else + { + // If no port is defined by the user, use the default value + empty($this->port) OR $this->port = 33000; + } + } + + // -------------------------------------------------------------------- + + /** + * Non-persistent database connection + * + * @param bool $persistent + * @return resource + */ + public function db_connect($persistent = FALSE) + { + if (preg_match('/^CUBRID:[^:]+(:[0-9][1-9]{0,4})?:[^:]+:([^:]*):([^:]*):(\?.+)?$/', $this->dsn, $matches)) + { + $func = ($persistent !== TRUE) ? 'cubrid_connect_with_url' : 'cubrid_pconnect_with_url'; + return ($matches[2] === '' && $matches[3] === '' && $this->username !== '' && $this->password !== '') + ? $func($this->dsn, $this->username, $this->password) + : $func($this->dsn); + } + + $func = ($persistent !== TRUE) ? 'cubrid_connect' : 'cubrid_pconnect'; + return ($this->username !== '') + ? $func($this->hostname, $this->port, $this->database, $this->username, $this->password) + : $func($this->hostname, $this->port, $this->database); + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout + * + * @return void + */ + public function reconnect() + { + if (cubrid_ping($this->conn_id) === FALSE) + { + $this->conn_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Database version number + * + * @return string + */ + public function version() + { + if (isset($this->data_cache['version'])) + { + return $this->data_cache['version']; + } + + return ( ! $this->conn_id OR ($version = cubrid_get_server_info($this->conn_id)) === FALSE) + ? FALSE + : $this->data_cache['version'] = $version; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @param string $sql an SQL query + * @return resource + */ + protected function _execute($sql) + { + return cubrid_query($sql, $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + protected function _trans_begin() + { + if (($autocommit = cubrid_get_autocommit($this->conn_id)) === NULL) + { + return FALSE; + } + elseif ($autocommit === TRUE) + { + return cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_FALSE); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + protected function _trans_commit() + { + if ( ! cubrid_commit($this->conn_id)) + { + return FALSE; + } + + if ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id)) + { + return cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + protected function _trans_rollback() + { + if ( ! cubrid_rollback($this->conn_id)) + { + return FALSE; + } + + if ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id)) + { + cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Platform-dependant string escape + * + * @param string + * @return string + */ + protected function _escape_str($str) + { + return cubrid_real_escape_string($str, $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @return int + */ + public function affected_rows() + { + return cubrid_affected_rows(); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @return int + */ + public function insert_id() + { + return cubrid_insert_id($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = 'SHOW TABLES'; + + if ($prefix_limit !== FALSE && $this->dbprefix !== '') + { + return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'"; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @param string $table + * @return string + */ + protected function _list_columns($table = '') + { + return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE); + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function field_data($table) + { + if (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE) + { + return FALSE; + } + $query = $query->result_object(); + + $retval = array(); + for ($i = 0, $c = count($query); $i < $c; $i++) + { + $retval[$i] = new stdClass(); + $retval[$i]->name = $query[$i]->Field; + + sscanf($query[$i]->Type, '%[a-z](%d)', + $retval[$i]->type, + $retval[$i]->max_length + ); + + $retval[$i]->default = $query[$i]->Default; + $retval[$i]->primary_key = (int) ($query[$i]->Key === 'PRI'); + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Error + * + * Returns an array containing code and message of the last + * database error that has occured. + * + * @return array + */ + public function error() + { + return array('code' => cubrid_errno($this->conn_id), 'message' => cubrid_error($this->conn_id)); + } + + // -------------------------------------------------------------------- + + /** + * FROM tables + * + * Groups tables in FROM clauses if needed, so there is no confusion + * about operator precedence. + * + * @return string + */ + protected function _from_tables() + { + if ( ! empty($this->qb_join) && count($this->qb_from) > 1) + { + return '('.implode(', ', $this->qb_from).')'; + } + + return implode(', ', $this->qb_from); + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @return void + */ + protected function _close() + { + cubrid_close($this->conn_id); + } + +} diff --git a/codeigniter-3.0/system/database/drivers/cubrid/cubrid_forge.php b/ci-3.0/system/database/drivers/cubrid/cubrid_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/cubrid/cubrid_forge.php rename to ci-3.0/system/database/drivers/cubrid/cubrid_forge.php diff --git a/codeigniter-3.0/system/database/drivers/cubrid/cubrid_result.php b/ci-3.0/system/database/drivers/cubrid/cubrid_result.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/cubrid/cubrid_result.php rename to ci-3.0/system/database/drivers/cubrid/cubrid_result.php diff --git a/codeigniter-3.0/system/database/drivers/cubrid/cubrid_utility.php b/ci-3.0/system/database/drivers/cubrid/cubrid_utility.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/cubrid/cubrid_utility.php rename to ci-3.0/system/database/drivers/cubrid/cubrid_utility.php diff --git a/codeigniter-3.0/system/database/drivers/cubrid/index.html b/ci-3.0/system/database/drivers/cubrid/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/cubrid/index.html rename to ci-3.0/system/database/drivers/cubrid/index.html diff --git a/ci-3.0/system/database/drivers/ibase/ibase_driver.php b/ci-3.0/system/database/drivers/ibase/ibase_driver.php new file mode 100755 index 000000000..82550d51b --- /dev/null +++ b/ci-3.0/system/database/drivers/ibase/ibase_driver.php @@ -0,0 +1,396 @@ +hostname.':'.$this->database, $this->username, $this->password, $this->char_set) + : ibase_connect($this->hostname.':'.$this->database, $this->username, $this->password, $this->char_set); + } + + // -------------------------------------------------------------------- + + /** + * Database version number + * + * @return string + */ + public function version() + { + if (isset($this->data_cache['version'])) + { + return $this->data_cache['version']; + } + + if (($service = ibase_service_attach($this->hostname, $this->username, $this->password))) + { + $this->data_cache['version'] = ibase_server_info($service, IBASE_SVC_SERVER_VERSION); + + // Don't keep the service open + ibase_service_detach($service); + return $this->data_cache['version']; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @param string $sql an SQL query + * @return resource + */ + protected function _execute($sql) + { + return ibase_query($this->conn_id, $sql); + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + protected function _trans_begin() + { + if (($trans_handle = ibase_trans($this->conn_id)) === FALSE) + { + return FALSE; + } + + $this->_ibase_trans = $trans_handle; + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + protected function _trans_commit() + { + if (ibase_commit($this->_ibase_trans)) + { + $this->_ibase_trans = NULL; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + protected function _trans_rollback() + { + if (ibase_rollback($this->_ibase_trans)) + { + $this->_ibase_trans = NULL; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @return int + */ + public function affected_rows() + { + return ibase_affected_rows($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @param string $generator_name + * @param int $inc_by + * @return int + */ + public function insert_id($generator_name, $inc_by = 0) + { + //If a generator hasn't been used before it will return 0 + return ibase_gen_id('"'.$generator_name.'"', $inc_by); + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = 'SELECT TRIM("RDB$RELATION_NAME") AS TABLE_NAME FROM "RDB$RELATIONS" WHERE "RDB$RELATION_NAME" NOT LIKE \'RDB$%\' AND "RDB$RELATION_NAME" NOT LIKE \'MON$%\''; + + if ($prefix_limit !== FALSE && $this->dbprefix !== '') + { + return $sql.' AND TRIM("RDB$RELATION_NAME") AS TABLE_NAME LIKE \''.$this->escape_like_str($this->dbprefix)."%' " + .sprintf($this->_like_escape_str, $this->_like_escape_chr); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @param string $table + * @return string + */ + protected function _list_columns($table = '') + { + return 'SELECT TRIM("RDB$FIELD_NAME") AS COLUMN_NAME FROM "RDB$RELATION_FIELDS" WHERE "RDB$RELATION_NAME" = '.$this->escape($table); + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function field_data($table) + { + $sql = 'SELECT "rfields"."RDB$FIELD_NAME" AS "name", + CASE "fields"."RDB$FIELD_TYPE" + WHEN 7 THEN \'SMALLINT\' + WHEN 8 THEN \'INTEGER\' + WHEN 9 THEN \'QUAD\' + WHEN 10 THEN \'FLOAT\' + WHEN 11 THEN \'DFLOAT\' + WHEN 12 THEN \'DATE\' + WHEN 13 THEN \'TIME\' + WHEN 14 THEN \'CHAR\' + WHEN 16 THEN \'INT64\' + WHEN 27 THEN \'DOUBLE\' + WHEN 35 THEN \'TIMESTAMP\' + WHEN 37 THEN \'VARCHAR\' + WHEN 40 THEN \'CSTRING\' + WHEN 261 THEN \'BLOB\' + ELSE NULL + END AS "type", + "fields"."RDB$FIELD_LENGTH" AS "max_length", + "rfields"."RDB$DEFAULT_VALUE" AS "default" + FROM "RDB$RELATION_FIELDS" "rfields" + JOIN "RDB$FIELDS" "fields" ON "rfields"."RDB$FIELD_SOURCE" = "fields"."RDB$FIELD_NAME" + WHERE "rfields"."RDB$RELATION_NAME" = '.$this->escape($table).' + ORDER BY "rfields"."RDB$FIELD_POSITION"'; + + return (($query = $this->query($sql)) !== FALSE) + ? $query->result_object() + : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Error + * + * Returns an array containing code and message of the last + * database error that has occured. + * + * @return array + */ + public function error() + { + return array('code' => ibase_errcode(), 'message' => ibase_errmsg()); + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @param string $table + * @param array $values + * @return string + */ + protected function _update($table, $values) + { + $this->qb_limit = FALSE; + return parent::_update($table, $values); + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * + * If the database does not support the TRUNCATE statement, + * then this method maps to 'DELETE FROM table' + * + * @param string $table + * @return string + */ + protected function _truncate($table) + { + return 'DELETE FROM '.$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @param string $table + * @return string + */ + protected function _delete($table) + { + $this->qb_limit = FALSE; + return parent::_delete($table); + } + + // -------------------------------------------------------------------- + + /** + * LIMIT + * + * Generates a platform-specific LIMIT clause + * + * @param string $sql SQL Query + * @return string + */ + protected function _limit($sql) + { + // Limit clause depends on if Interbase or Firebird + if (stripos($this->version(), 'firebird') !== FALSE) + { + $select = 'FIRST '.$this->qb_limit + .($this->qb_offset ? ' SKIP '.$this->qb_offset : ''); + } + else + { + $select = 'ROWS ' + .($this->qb_offset ? $this->qb_offset.' TO '.($this->qb_limit + $this->qb_offset) : $this->qb_limit); + } + + return preg_replace('`SELECT`i', 'SELECT '.$select, $sql, 1); + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @return void + */ + protected function _close() + { + ibase_close($this->conn_id); + } + +} diff --git a/codeigniter-3.0/system/database/drivers/ibase/ibase_forge.php b/ci-3.0/system/database/drivers/ibase/ibase_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/ibase/ibase_forge.php rename to ci-3.0/system/database/drivers/ibase/ibase_forge.php diff --git a/codeigniter-3.0/system/database/drivers/ibase/ibase_result.php b/ci-3.0/system/database/drivers/ibase/ibase_result.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/ibase/ibase_result.php rename to ci-3.0/system/database/drivers/ibase/ibase_result.php diff --git a/codeigniter-3.0/system/database/drivers/ibase/ibase_utility.php b/ci-3.0/system/database/drivers/ibase/ibase_utility.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/ibase/ibase_utility.php rename to ci-3.0/system/database/drivers/ibase/ibase_utility.php diff --git a/codeigniter-3.0/system/database/drivers/ibase/index.html b/ci-3.0/system/database/drivers/ibase/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/ibase/index.html rename to ci-3.0/system/database/drivers/ibase/index.html diff --git a/codeigniter-3.0/system/database/drivers/index.html b/ci-3.0/system/database/drivers/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/index.html rename to ci-3.0/system/database/drivers/index.html diff --git a/codeigniter-3.0/system/database/drivers/mssql/index.html b/ci-3.0/system/database/drivers/mssql/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/mssql/index.html rename to ci-3.0/system/database/drivers/mssql/index.html diff --git a/ci-3.0/system/database/drivers/mssql/mssql_driver.php b/ci-3.0/system/database/drivers/mssql/mssql_driver.php new file mode 100755 index 000000000..883973ae1 --- /dev/null +++ b/ci-3.0/system/database/drivers/mssql/mssql_driver.php @@ -0,0 +1,517 @@ +port)) + { + $this->hostname .= (DIRECTORY_SEPARATOR === '\\' ? ',' : ':').$this->port; + } + } + + // -------------------------------------------------------------------- + + /** + * Non-persistent database connection + * + * @param bool $persistent + * @return resource + */ + public function db_connect($persistent = FALSE) + { + $this->conn_id = ($persistent) + ? mssql_pconnect($this->hostname, $this->username, $this->password) + : mssql_connect($this->hostname, $this->username, $this->password); + + if ( ! $this->conn_id) + { + return FALSE; + } + + // ---------------------------------------------------------------- + + // Select the DB... assuming a database name is specified in the config file + if ($this->database !== '' && ! $this->db_select()) + { + log_message('error', 'Unable to select database: '.$this->database); + + return ($this->db_debug === TRUE) + ? $this->display_error('db_unable_to_select', $this->database) + : FALSE; + } + + // Determine how identifiers are escaped + $query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi'); + $query = $query->row_array(); + $this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi']; + $this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']'); + + return $this->conn_id; + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @param string $database + * @return bool + */ + public function db_select($database = '') + { + if ($database === '') + { + $database = $this->database; + } + + // Note: Escaping is required in the event that the DB name + // contains reserved characters. + if (mssql_select_db('['.$database.']', $this->conn_id)) + { + $this->database = $database; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @param string $sql an SQL query + * @return mixed resource if rows are returned, bool otherwise + */ + protected function _execute($sql) + { + return mssql_query($sql, $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + protected function _trans_begin() + { + return $this->simple_query('BEGIN TRAN'); + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + protected function _trans_commit() + { + return $this->simple_query('COMMIT TRAN'); + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + protected function _trans_rollback() + { + return $this->simple_query('ROLLBACK TRAN'); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @return int + */ + public function affected_rows() + { + return mssql_rows_affected($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * Returns the last id created in the Identity column. + * + * @return string + */ + public function insert_id() + { + $query = version_compare($this->version(), '8', '>=') + ? 'SELECT SCOPE_IDENTITY() AS last_id' + : 'SELECT @@IDENTITY AS last_id'; + + $query = $this->query($query); + $query = $query->row(); + return $query->last_id; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @param string $charset + * @return bool + */ + protected function _db_set_charset($charset) + { + return (ini_set('mssql.charset', $charset) !== FALSE); + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @return string + */ + protected function _version() + { + return 'SELECT @@VERSION AS ver'; + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = 'SELECT '.$this->escape_identifiers('name') + .' FROM '.$this->escape_identifiers('sysobjects') + .' WHERE '.$this->escape_identifiers('type')." = 'U'"; + + if ($prefix_limit !== FALSE && $this->dbprefix !== '') + { + $sql .= ' AND '.$this->escape_identifiers('name')." LIKE '".$this->escape_like_str($this->dbprefix)."%' " + .sprintf($this->_like_escape_str, $this->_like_escape_chr); + } + + return $sql.' ORDER BY '.$this->escape_identifiers('name'); + } + + // -------------------------------------------------------------------- + + /** + * List column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @param string $table + * @return string + */ + protected function _list_columns($table = '') + { + return 'SELECT COLUMN_NAME + FROM INFORMATION_SCHEMA.Columns + WHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table)); + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function field_data($table) + { + $sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, COLUMN_DEFAULT + FROM INFORMATION_SCHEMA.Columns + WHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table)); + + if (($query = $this->query($sql)) === FALSE) + { + return FALSE; + } + $query = $query->result_object(); + + $retval = array(); + for ($i = 0, $c = count($query); $i < $c; $i++) + { + $retval[$i] = new stdClass(); + $retval[$i]->name = $query[$i]->COLUMN_NAME; + $retval[$i]->type = $query[$i]->DATA_TYPE; + $retval[$i]->max_length = ($query[$i]->CHARACTER_MAXIMUM_LENGTH > 0) ? $query[$i]->CHARACTER_MAXIMUM_LENGTH : $query[$i]->NUMERIC_PRECISION; + $retval[$i]->default = $query[$i]->COLUMN_DEFAULT; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Error + * + * Returns an array containing code and message of the last + * database error that has occured. + * + * @return array + */ + public function error() + { + // We need this because the error info is discarded by the + // server the first time you request it, and query() already + // calls error() once for logging purposes when a query fails. + static $error = array('code' => 0, 'message' => NULL); + + $message = mssql_get_last_message(); + if ( ! empty($message)) + { + $error['code'] = $this->query('SELECT @@ERROR AS code')->row()->code; + $error['message'] = $message; + } + + return $error; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @param string $table + * @param array $values + * @return string + */ + protected function _update($table, $values) + { + $this->qb_limit = FALSE; + $this->qb_orderby = array(); + return parent::_update($table, $values); + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * + * If the database does not support the TRUNCATE statement, + * then this method maps to 'DELETE FROM table' + * + * @param string $table + * @return string + */ + protected function _truncate($table) + { + return 'TRUNCATE TABLE '.$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @param string $table + * @return string + */ + protected function _delete($table) + { + if ($this->qb_limit) + { + return 'WITH ci_delete AS (SELECT TOP '.$this->qb_limit.' * FROM '.$table.$this->_compile_wh('qb_where').') DELETE FROM ci_delete'; + } + + return parent::_delete($table); + } + + // -------------------------------------------------------------------- + + /** + * LIMIT + * + * Generates a platform-specific LIMIT clause + * + * @param string $sql SQL Query + * @return string + */ + protected function _limit($sql) + { + $limit = $this->qb_offset + $this->qb_limit; + + // As of SQL Server 2005 (9.0.*) ROW_NUMBER() is supported, + // however an ORDER BY clause is required for it to work + if (version_compare($this->version(), '9', '>=') && $this->qb_offset && ! empty($this->qb_orderby)) + { + $orderby = $this->_compile_order_by(); + + // We have to strip the ORDER BY clause + $sql = trim(substr($sql, 0, strrpos($sql, $orderby))); + + // Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results + if (count($this->qb_select) === 0) + { + $select = '*'; // Inevitable + } + else + { + // Use only field names and their aliases, everything else is out of our scope. + $select = array(); + $field_regexp = ($this->_quoted_identifier) + ? '("[^\"]+")' : '(\[[^\]]+\])'; + for ($i = 0, $c = count($this->qb_select); $i < $c; $i++) + { + $select[] = preg_match('/(?:\s|\.)'.$field_regexp.'$/i', $this->qb_select[$i], $m) + ? $m[1] : $this->qb_select[$i]; + } + $select = implode(', ', $select); + } + + return 'SELECT '.$select." FROM (\n\n" + .preg_replace('/^(SELECT( DISTINCT)?)/i', '\\1 ROW_NUMBER() OVER('.trim($orderby).') AS '.$this->escape_identifiers('CI_rownum').', ', $sql) + ."\n\n) ".$this->escape_identifiers('CI_subquery') + ."\nWHERE ".$this->escape_identifiers('CI_rownum').' BETWEEN '.($this->qb_offset + 1).' AND '.$limit; + } + + return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql); + } + + // -------------------------------------------------------------------- + + /** + * Insert batch statement + * + * Generates a platform-specific insert string from the supplied data. + * + * @param string $table Table name + * @param array $keys INSERT keys + * @param array $values INSERT values + * @return string|bool + */ + protected function _insert_batch($table, $keys, $values) + { + // Multiple-value inserts are only supported as of SQL Server 2008 + if (version_compare($this->version(), '10', '>=')) + { + return parent::_insert_batch($table, $keys, $values); + } + + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @return void + */ + protected function _close() + { + mssql_close($this->conn_id); + } + +} diff --git a/codeigniter-3.0/system/database/drivers/mssql/mssql_forge.php b/ci-3.0/system/database/drivers/mssql/mssql_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/mssql/mssql_forge.php rename to ci-3.0/system/database/drivers/mssql/mssql_forge.php diff --git a/codeigniter-3.0/system/database/drivers/mssql/mssql_result.php b/ci-3.0/system/database/drivers/mssql/mssql_result.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/mssql/mssql_result.php rename to ci-3.0/system/database/drivers/mssql/mssql_result.php diff --git a/codeigniter-3.0/system/database/drivers/mssql/mssql_utility.php b/ci-3.0/system/database/drivers/mssql/mssql_utility.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/mssql/mssql_utility.php rename to ci-3.0/system/database/drivers/mssql/mssql_utility.php diff --git a/codeigniter-3.0/system/database/drivers/mysql/index.html b/ci-3.0/system/database/drivers/mysql/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/mysql/index.html rename to ci-3.0/system/database/drivers/mysql/index.html diff --git a/ci-3.0/system/database/drivers/mysql/mysql_driver.php b/ci-3.0/system/database/drivers/mysql/mysql_driver.php new file mode 100755 index 000000000..9c630d0d6 --- /dev/null +++ b/ci-3.0/system/database/drivers/mysql/mysql_driver.php @@ -0,0 +1,476 @@ +port)) + { + $this->hostname .= ':'.$this->port; + } + } + + // -------------------------------------------------------------------- + + /** + * Non-persistent database connection + * + * @param bool $persistent + * @return resource + */ + public function db_connect($persistent = FALSE) + { + $client_flags = ($this->compress === FALSE) ? 0 : MYSQL_CLIENT_COMPRESS; + + if ($this->encrypt === TRUE) + { + $client_flags = $client_flags | MYSQL_CLIENT_SSL; + } + + // Error suppression is necessary mostly due to PHP 5.5+ issuing E_DEPRECATED messages + $this->conn_id = ($persistent === TRUE) + ? mysql_pconnect($this->hostname, $this->username, $this->password, $client_flags) + : mysql_connect($this->hostname, $this->username, $this->password, TRUE, $client_flags); + + // ---------------------------------------------------------------- + + // Select the DB... assuming a database name is specified in the config file + if ($this->database !== '' && ! $this->db_select()) + { + log_message('error', 'Unable to select database: '.$this->database); + + return ($this->db_debug === TRUE) + ? $this->display_error('db_unable_to_select', $this->database) + : FALSE; + } + + if ($this->stricton && is_resource($this->conn_id)) + { + $this->simple_query('SET SESSION sql_mode="STRICT_ALL_TABLES"'); + } + + return $this->conn_id; + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout + * + * @return void + */ + public function reconnect() + { + if (mysql_ping($this->conn_id) === FALSE) + { + $this->conn_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @param string $database + * @return bool + */ + public function db_select($database = '') + { + if ($database === '') + { + $database = $this->database; + } + + if (mysql_select_db($database, $this->conn_id)) + { + $this->database = $database; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @param string $charset + * @return bool + */ + protected function _db_set_charset($charset) + { + return mysql_set_charset($charset, $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Database version number + * + * @return string + */ + public function version() + { + if (isset($this->data_cache['version'])) + { + return $this->data_cache['version']; + } + + if ( ! $this->conn_id OR ($version = mysql_get_server_info($this->conn_id)) === FALSE) + { + return FALSE; + } + + return $this->data_cache['version'] = $version; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @param string $sql an SQL query + * @return mixed + */ + protected function _execute($sql) + { + return mysql_query($this->_prep_query($sql), $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @param string $sql an SQL query + * @return string + */ + protected function _prep_query($sql) + { + // mysql_affected_rows() returns 0 for "DELETE FROM TABLE" queries. This hack + // modifies the query so that it a proper number of affected rows is returned. + if ($this->delete_hack === TRUE && preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql)) + { + return trim($sql).' WHERE 1=1'; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + protected function _trans_begin() + { + $this->simple_query('SET AUTOCOMMIT=0'); + return $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + protected function _trans_commit() + { + if ($this->simple_query('COMMIT')) + { + $this->simple_query('SET AUTOCOMMIT=1'); + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + protected function _trans_rollback() + { + if ($this->simple_query('ROLLBACK')) + { + $this->simple_query('SET AUTOCOMMIT=1'); + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Platform-dependant string escape + * + * @param string + * @return string + */ + protected function _escape_str($str) + { + return mysql_real_escape_string($str, $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @return int + */ + public function affected_rows() + { + return mysql_affected_rows($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @return int + */ + public function insert_id() + { + return mysql_insert_id($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = 'SHOW TABLES FROM '.$this->escape_identifiers($this->database); + + if ($prefix_limit !== FALSE && $this->dbprefix !== '') + { + return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'"; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @param string $table + * @return string + */ + protected function _list_columns($table = '') + { + return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE); + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function field_data($table) + { + if (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE) + { + return FALSE; + } + $query = $query->result_object(); + + $retval = array(); + for ($i = 0, $c = count($query); $i < $c; $i++) + { + $retval[$i] = new stdClass(); + $retval[$i]->name = $query[$i]->Field; + + sscanf($query[$i]->Type, '%[a-z](%d)', + $retval[$i]->type, + $retval[$i]->max_length + ); + + $retval[$i]->default = $query[$i]->Default; + $retval[$i]->primary_key = (int) ($query[$i]->Key === 'PRI'); + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Error + * + * Returns an array containing code and message of the last + * database error that has occured. + * + * @return array + */ + public function error() + { + return array('code' => mysql_errno($this->conn_id), 'message' => mysql_error($this->conn_id)); + } + + // -------------------------------------------------------------------- + + /** + * FROM tables + * + * Groups tables in FROM clauses if needed, so there is no confusion + * about operator precedence. + * + * @return string + */ + protected function _from_tables() + { + if ( ! empty($this->qb_join) && count($this->qb_from) > 1) + { + return '('.implode(', ', $this->qb_from).')'; + } + + return implode(', ', $this->qb_from); + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @return void + */ + protected function _close() + { + // Error suppression to avoid annoying E_WARNINGs in cases + // where the connection has already been closed for some reason. + @mysql_close($this->conn_id); + } + +} diff --git a/codeigniter-3.0/system/database/drivers/mysql/mysql_forge.php b/ci-3.0/system/database/drivers/mysql/mysql_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/mysql/mysql_forge.php rename to ci-3.0/system/database/drivers/mysql/mysql_forge.php diff --git a/codeigniter-3.0/system/database/drivers/mysql/mysql_result.php b/ci-3.0/system/database/drivers/mysql/mysql_result.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/mysql/mysql_result.php rename to ci-3.0/system/database/drivers/mysql/mysql_result.php diff --git a/codeigniter-3.0/system/database/drivers/mysql/mysql_utility.php b/ci-3.0/system/database/drivers/mysql/mysql_utility.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/mysql/mysql_utility.php rename to ci-3.0/system/database/drivers/mysql/mysql_utility.php diff --git a/codeigniter-3.0/system/database/drivers/mysqli/index.html b/ci-3.0/system/database/drivers/mysqli/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/mysqli/index.html rename to ci-3.0/system/database/drivers/mysqli/index.html diff --git a/ci-3.0/system/database/drivers/mysqli/mysqli_driver.php b/ci-3.0/system/database/drivers/mysqli/mysqli_driver.php new file mode 100755 index 000000000..827470078 --- /dev/null +++ b/ci-3.0/system/database/drivers/mysqli/mysqli_driver.php @@ -0,0 +1,503 @@ +hostname[0] === '/') + { + $hostname = NULL; + $port = NULL; + $socket = $this->hostname; + } + else + { + // Persistent connection support was added in PHP 5.3.0 + $hostname = ($persistent === TRUE && is_php('5.3')) + ? 'p:'.$this->hostname : $this->hostname; + $port = empty($this->port) ? NULL : $this->port; + $socket = NULL; + } + + $client_flags = ($this->compress === TRUE) ? MYSQLI_CLIENT_COMPRESS : 0; + $mysqli = mysqli_init(); + + $mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 10); + + if ($this->stricton) + { + $mysqli->options(MYSQLI_INIT_COMMAND, 'SET SESSION sql_mode="STRICT_ALL_TABLES"'); + } + + if (is_array($this->encrypt)) + { + $ssl = array(); + empty($this->encrypt['ssl_key']) OR $ssl['key'] = $this->encrypt['ssl_key']; + empty($this->encrypt['ssl_cert']) OR $ssl['cert'] = $this->encrypt['ssl_cert']; + empty($this->encrypt['ssl_ca']) OR $ssl['ca'] = $this->encrypt['ssl_ca']; + empty($this->encrypt['ssl_capath']) OR $ssl['capath'] = $this->encrypt['ssl_capath']; + empty($this->encrypt['ssl_cipher']) OR $ssl['cipher'] = $this->encrypt['ssl_cipher']; + + if ( ! empty($ssl)) + { + if ( ! empty($this->encrypt['ssl_verify']) && defined('MYSQLI_OPT_SSL_VERIFY_SERVER_CERT')) + { + $mysqli->options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, TRUE); + } + + $client_flags |= MYSQLI_CLIENT_SSL; + $mysqli->ssl_set( + isset($ssl['key']) ? $ssl['key'] : NULL, + isset($ssl['cert']) ? $ssl['cert'] : NULL, + isset($ssl['ca']) ? $ssl['ca'] : NULL, + isset($ssl['capath']) ? $ssl['capath'] : NULL, + isset($ssl['cipher']) ? $ssl['cipher'] : NULL + ); + } + } + + if ($mysqli->real_connect($hostname, $this->username, $this->password, $this->database, $port, $socket, $client_flags)) + { + // Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails + if ( + ($client_flags & MYSQLI_CLIENT_SSL) + && version_compare($mysqli->client_info, '5.7.3', '<=') + && empty($mysqli->query("SHOW STATUS LIKE 'ssl_cipher'")->fetch_object()->Value) + ) + { + $mysqli->close(); + $message = 'MySQLi was configured for an SSL connection, but got an unencrypted connection instead!'; + log_message('error', $message); + return ($this->db->db_debug) ? $this->db->display_error($message, '', TRUE) : FALSE; + } + + return $mysqli; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout + * + * @return void + */ + public function reconnect() + { + if ($this->conn_id !== FALSE && $this->conn_id->ping() === FALSE) + { + $this->conn_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @param string $database + * @return bool + */ + public function db_select($database = '') + { + if ($database === '') + { + $database = $this->database; + } + + if ($this->conn_id->select_db($database)) + { + $this->database = $database; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @param string $charset + * @return bool + */ + protected function _db_set_charset($charset) + { + return $this->conn_id->set_charset($charset); + } + + // -------------------------------------------------------------------- + + /** + * Database version number + * + * @return string + */ + public function version() + { + if (isset($this->data_cache['version'])) + { + return $this->data_cache['version']; + } + + return $this->data_cache['version'] = $this->conn_id->server_info; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @param string $sql an SQL query + * @return mixed + */ + protected function _execute($sql) + { + return $this->conn_id->query($this->_prep_query($sql)); + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @param string $sql an SQL query + * @return string + */ + protected function _prep_query($sql) + { + // mysqli_affected_rows() returns 0 for "DELETE FROM TABLE" queries. This hack + // modifies the query so that it a proper number of affected rows is returned. + if ($this->delete_hack === TRUE && preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql)) + { + return trim($sql).' WHERE 1=1'; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + protected function _trans_begin() + { + $this->conn_id->autocommit(FALSE); + return is_php('5.5') + ? $this->conn_id->begin_transaction() + : $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + protected function _trans_commit() + { + if ($this->conn_id->commit()) + { + $this->conn_id->autocommit(TRUE); + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + protected function _trans_rollback() + { + if ($this->conn_id->rollback()) + { + $this->conn_id->autocommit(TRUE); + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Platform-dependant string escape + * + * @param string + * @return string + */ + protected function _escape_str($str) + { + return $this->conn_id->real_escape_string($str); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @return int + */ + public function affected_rows() + { + return $this->conn_id->affected_rows; + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @return int + */ + public function insert_id() + { + return $this->conn_id->insert_id; + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = 'SHOW TABLES FROM '.$this->escape_identifiers($this->database); + + if ($prefix_limit !== FALSE && $this->dbprefix !== '') + { + return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'"; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @param string $table + * @return string + */ + protected function _list_columns($table = '') + { + return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE); + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function field_data($table) + { + if (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE) + { + return FALSE; + } + $query = $query->result_object(); + + $retval = array(); + for ($i = 0, $c = count($query); $i < $c; $i++) + { + $retval[$i] = new stdClass(); + $retval[$i]->name = $query[$i]->Field; + + sscanf($query[$i]->Type, '%[a-z](%d)', + $retval[$i]->type, + $retval[$i]->max_length + ); + + $retval[$i]->default = $query[$i]->Default; + $retval[$i]->primary_key = (int) ($query[$i]->Key === 'PRI'); + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Error + * + * Returns an array containing code and message of the last + * database error that has occurred. + * + * @return array + */ + public function error() + { + if ( ! empty($this->conn_id->connect_errno)) + { + return array( + 'code' => $this->conn_id->connect_errno, + 'message' => is_php('5.2.9') ? $this->conn_id->connect_error : mysqli_connect_error() + ); + } + + return array('code' => $this->conn_id->errno, 'message' => $this->conn_id->error); + } + + // -------------------------------------------------------------------- + + /** + * FROM tables + * + * Groups tables in FROM clauses if needed, so there is no confusion + * about operator precedence. + * + * @return string + */ + protected function _from_tables() + { + if ( ! empty($this->qb_join) && count($this->qb_from) > 1) + { + return '('.implode(', ', $this->qb_from).')'; + } + + return implode(', ', $this->qb_from); + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @return void + */ + protected function _close() + { + $this->conn_id->close(); + } + +} diff --git a/codeigniter-3.0/system/database/drivers/mysqli/mysqli_forge.php b/ci-3.0/system/database/drivers/mysqli/mysqli_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/mysqli/mysqli_forge.php rename to ci-3.0/system/database/drivers/mysqli/mysqli_forge.php diff --git a/codeigniter-3.0/system/database/drivers/mysqli/mysqli_result.php b/ci-3.0/system/database/drivers/mysqli/mysqli_result.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/mysqli/mysqli_result.php rename to ci-3.0/system/database/drivers/mysqli/mysqli_result.php diff --git a/codeigniter-3.0/system/database/drivers/mysqli/mysqli_utility.php b/ci-3.0/system/database/drivers/mysqli/mysqli_utility.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/mysqli/mysqli_utility.php rename to ci-3.0/system/database/drivers/mysqli/mysqli_utility.php diff --git a/codeigniter-3.0/system/database/drivers/oci8/index.html b/ci-3.0/system/database/drivers/oci8/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/oci8/index.html rename to ci-3.0/system/database/drivers/oci8/index.html diff --git a/ci-3.0/system/database/drivers/oci8/oci8_driver.php b/ci-3.0/system/database/drivers/oci8/oci8_driver.php new file mode 100755 index 000000000..916ddeb90 --- /dev/null +++ b/ci-3.0/system/database/drivers/oci8/oci8_driver.php @@ -0,0 +1,670 @@ + '/^\(DESCRIPTION=(\(.+\)){2,}\)$/', // TNS + // Easy Connect string (Oracle 10g+) + 'ec' => '/^(\/\/)?[a-z0-9.:_-]+(:[1-9][0-9]{0,4})?(\/[a-z0-9$_]+)?(:[^\/])?(\/[a-z0-9$_]+)?$/i', + 'in' => '/^[a-z0-9$_]+$/i' // Instance name (defined in tnsnames.ora) + ); + + /* Space characters don't have any effect when actually + * connecting, but can be a hassle while validating the DSN. + */ + $this->dsn = str_replace(array("\n", "\r", "\t", ' '), '', $this->dsn); + + if ($this->dsn !== '') + { + foreach ($valid_dsns as $regexp) + { + if (preg_match($regexp, $this->dsn)) + { + return; + } + } + } + + // Legacy support for TNS in the hostname configuration field + $this->hostname = str_replace(array("\n", "\r", "\t", ' '), '', $this->hostname); + if (preg_match($valid_dsns['tns'], $this->hostname)) + { + $this->dsn = $this->hostname; + return; + } + elseif ($this->hostname !== '' && strpos($this->hostname, '/') === FALSE && strpos($this->hostname, ':') === FALSE + && (( ! empty($this->port) && ctype_digit($this->port)) OR $this->database !== '')) + { + /* If the hostname field isn't empty, doesn't contain + * ':' and/or '/' and if port and/or database aren't + * empty, then the hostname field is most likely indeed + * just a hostname. Therefore we'll try and build an + * Easy Connect string from these 3 settings, assuming + * that the database field is a service name. + */ + $this->dsn = $this->hostname + .(( ! empty($this->port) && ctype_digit($this->port)) ? ':'.$this->port : '') + .($this->database !== '' ? '/'.ltrim($this->database, '/') : ''); + + if (preg_match($valid_dsns['ec'], $this->dsn)) + { + return; + } + } + + /* At this point, we can only try and validate the hostname and + * database fields separately as DSNs. + */ + if (preg_match($valid_dsns['ec'], $this->hostname) OR preg_match($valid_dsns['in'], $this->hostname)) + { + $this->dsn = $this->hostname; + return; + } + + $this->database = str_replace(array("\n", "\r", "\t", ' '), '', $this->database); + foreach ($valid_dsns as $regexp) + { + if (preg_match($regexp, $this->database)) + { + return; + } + } + + /* Well - OK, an empty string should work as well. + * PHP will try to use environment variables to + * determine which Oracle instance to connect to. + */ + $this->dsn = ''; + } + + // -------------------------------------------------------------------- + + /** + * Non-persistent database connection + * + * @param bool $persistent + * @return resource + */ + public function db_connect($persistent = FALSE) + { + $func = ($persistent === TRUE) ? 'oci_pconnect' : 'oci_connect'; + return empty($this->char_set) + ? $func($this->username, $this->password, $this->dsn) + : $func($this->username, $this->password, $this->dsn, $this->char_set); + } + + // -------------------------------------------------------------------- + + /** + * Database version number + * + * @return string + */ + public function version() + { + if (isset($this->data_cache['version'])) + { + return $this->data_cache['version']; + } + + if ( ! $this->conn_id OR ($version = oci_server_version($this->conn_id)) === FALSE) + { + return FALSE; + } + + return $this->data_cache['version'] = $version; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @param string $sql an SQL query + * @return resource + */ + protected function _execute($sql) + { + /* Oracle must parse the query before it is run. All of the actions with + * the query are based on the statement id returned by oci_parse(). + */ + if ($this->_reset_stmt_id === TRUE) + { + $this->stmt_id = oci_parse($this->conn_id, $sql); + } + + oci_set_prefetch($this->stmt_id, 1000); + return oci_execute($this->stmt_id, $this->commit_mode); + } + + // -------------------------------------------------------------------- + + /** + * Get cursor. Returns a cursor from the database + * + * @return resource + */ + public function get_cursor() + { + return $this->curs_id = oci_new_cursor($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Stored Procedure. Executes a stored procedure + * + * @param string package name in which the stored procedure is in + * @param string stored procedure name to execute + * @param array parameters + * @return mixed + * + * params array keys + * + * KEY OPTIONAL NOTES + * name no the name of the parameter should be in : format + * value no the value of the parameter. If this is an OUT or IN OUT parameter, + * this should be a reference to a variable + * type yes the type of the parameter + * length yes the max size of the parameter + */ + public function stored_procedure($package, $procedure, array $params) + { + if ($package === '' OR $procedure === '') + { + log_message('error', 'Invalid query: '.$package.'.'.$procedure); + return ($this->db_debug) ? $this->display_error('db_invalid_query') : FALSE; + } + + // Build the query string + $sql = 'BEGIN '.$package.'.'.$procedure.'('; + + $have_cursor = FALSE; + foreach ($params as $param) + { + $sql .= $param['name'].','; + + if (isset($param['type']) && $param['type'] === OCI_B_CURSOR) + { + $have_cursor = TRUE; + } + } + $sql = trim($sql, ',').'); END;'; + + $this->_reset_stmt_id = FALSE; + $this->stmt_id = oci_parse($this->conn_id, $sql); + $this->_bind_params($params); + $result = $this->query($sql, FALSE, $have_cursor); + $this->_reset_stmt_id = TRUE; + return $result; + } + + // -------------------------------------------------------------------- + + /** + * Bind parameters + * + * @param array $params + * @return void + */ + protected function _bind_params($params) + { + if ( ! is_array($params) OR ! is_resource($this->stmt_id)) + { + return; + } + + foreach ($params as $param) + { + foreach (array('name', 'value', 'type', 'length') as $val) + { + if ( ! isset($param[$val])) + { + $param[$val] = ''; + } + } + + oci_bind_by_name($this->stmt_id, $param['name'], $param['value'], $param['length'], $param['type']); + } + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + protected function _trans_begin() + { + $this->commit_mode = is_php('5.3.2') ? OCI_NO_AUTO_COMMIT : OCI_DEFAULT; + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + protected function _trans_commit() + { + $this->commit_mode = OCI_COMMIT_ON_SUCCESS; + + return oci_commit($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + protected function _trans_rollback() + { + $this->commit_mode = OCI_COMMIT_ON_SUCCESS; + return oci_rollback($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @return int + */ + public function affected_rows() + { + return oci_num_rows($this->stmt_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @return int + */ + public function insert_id() + { + // not supported in oracle + return $this->display_error('db_unsupported_function'); + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = 'SELECT "TABLE_NAME" FROM "ALL_TABLES"'; + + if ($prefix_limit !== FALSE && $this->dbprefix !== '') + { + return $sql.' WHERE "TABLE_NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' " + .sprintf($this->_like_escape_str, $this->_like_escape_chr); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @param string $table + * @return string + */ + protected function _list_columns($table = '') + { + if (strpos($table, '.') !== FALSE) + { + sscanf($table, '%[^.].%s', $owner, $table); + } + else + { + $owner = $this->username; + } + + return 'SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS + WHERE UPPER(OWNER) = '.$this->escape(strtoupper($owner)).' + AND UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table)); + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function field_data($table) + { + if (strpos($table, '.') !== FALSE) + { + sscanf($table, '%[^.].%s', $owner, $table); + } + else + { + $owner = $this->username; + } + + $sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHAR_LENGTH, DATA_PRECISION, DATA_LENGTH, DATA_DEFAULT, NULLABLE + FROM ALL_TAB_COLUMNS + WHERE UPPER(OWNER) = '.$this->escape(strtoupper($owner)).' + AND UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table)); + + if (($query = $this->query($sql)) === FALSE) + { + return FALSE; + } + $query = $query->result_object(); + + $retval = array(); + for ($i = 0, $c = count($query); $i < $c; $i++) + { + $retval[$i] = new stdClass(); + $retval[$i]->name = $query[$i]->COLUMN_NAME; + $retval[$i]->type = $query[$i]->DATA_TYPE; + + $length = ($query[$i]->CHAR_LENGTH > 0) + ? $query[$i]->CHAR_LENGTH : $query[$i]->DATA_PRECISION; + if ($length === NULL) + { + $length = $query[$i]->DATA_LENGTH; + } + $retval[$i]->max_length = $length; + + $default = $query[$i]->DATA_DEFAULT; + if ($default === NULL && $query[$i]->NULLABLE === 'N') + { + $default = ''; + } + $retval[$i]->default = $default; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Error + * + * Returns an array containing code and message of the last + * database error that has occured. + * + * @return array + */ + public function error() + { + /* oci_error() returns an array that already contains the + * 'code' and 'message' keys, so we can just return it. + */ + if (is_resource($this->curs_id)) + { + return oci_error($this->curs_id); + } + elseif (is_resource($this->stmt_id)) + { + return oci_error($this->stmt_id); + } + elseif (is_resource($this->conn_id)) + { + return oci_error($this->conn_id); + } + + return oci_error(); + } + + // -------------------------------------------------------------------- + + /** + * Insert batch statement + * + * Generates a platform-specific insert string from the supplied data + * + * @param string $table Table name + * @param array $keys INSERT keys + * @param array $values INSERT values + * @return string + */ + protected function _insert_batch($table, $keys, $values) + { + $keys = implode(', ', $keys); + $sql = "INSERT ALL\n"; + + for ($i = 0, $c = count($values); $i < $c; $i++) + { + $sql .= ' INTO '.$table.' ('.$keys.') VALUES '.$values[$i]."\n"; + } + + return $sql.'SELECT * FROM dual'; + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * + * If the database does not support the TRUNCATE statement, + * then this method maps to 'DELETE FROM table' + * + * @param string $table + * @return string + */ + protected function _truncate($table) + { + return 'TRUNCATE TABLE '.$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @param string $table + * @return string + */ + protected function _delete($table) + { + if ($this->qb_limit) + { + $this->where('rownum <= ',$this->qb_limit, FALSE); + $this->qb_limit = FALSE; + } + + return parent::_delete($table); + } + + // -------------------------------------------------------------------- + + /** + * LIMIT + * + * Generates a platform-specific LIMIT clause + * + * @param string $sql SQL Query + * @return string + */ + protected function _limit($sql) + { + $this->limit_used = TRUE; + return 'SELECT * FROM (SELECT inner_query.*, rownum rnum FROM ('.$sql.') inner_query WHERE rownum < '.($this->qb_offset + $this->qb_limit + 1).')' + .($this->qb_offset ? ' WHERE rnum >= '.($this->qb_offset + 1) : ''); + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @return void + */ + protected function _close() + { + oci_close($this->conn_id); + } + +} diff --git a/codeigniter-3.0/system/database/drivers/oci8/oci8_forge.php b/ci-3.0/system/database/drivers/oci8/oci8_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/oci8/oci8_forge.php rename to ci-3.0/system/database/drivers/oci8/oci8_forge.php diff --git a/codeigniter-3.0/system/database/drivers/oci8/oci8_result.php b/ci-3.0/system/database/drivers/oci8/oci8_result.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/oci8/oci8_result.php rename to ci-3.0/system/database/drivers/oci8/oci8_result.php diff --git a/codeigniter-3.0/system/database/drivers/oci8/oci8_utility.php b/ci-3.0/system/database/drivers/oci8/oci8_utility.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/oci8/oci8_utility.php rename to ci-3.0/system/database/drivers/oci8/oci8_utility.php diff --git a/codeigniter-3.0/system/database/drivers/odbc/index.html b/ci-3.0/system/database/drivers/odbc/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/odbc/index.html rename to ci-3.0/system/database/drivers/odbc/index.html diff --git a/ci-3.0/system/database/drivers/odbc/odbc_driver.php b/ci-3.0/system/database/drivers/odbc/odbc_driver.php new file mode 100755 index 000000000..409284b44 --- /dev/null +++ b/ci-3.0/system/database/drivers/odbc/odbc_driver.php @@ -0,0 +1,358 @@ +dsn)) + { + $this->dsn = $this->hostname; + } + } + + // -------------------------------------------------------------------- + + /** + * Non-persistent database connection + * + * @param bool $persistent + * @return resource + */ + public function db_connect($persistent = FALSE) + { + return ($persistent === TRUE) + ? odbc_pconnect($this->dsn, $this->username, $this->password) + : odbc_connect($this->dsn, $this->username, $this->password); + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @param string $sql an SQL query + * @return resource + */ + protected function _execute($sql) + { + return odbc_exec($this->conn_id, $sql); + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + protected function _trans_begin() + { + return odbc_autocommit($this->conn_id, FALSE); + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + protected function _trans_commit() + { + if (odbc_commit($this->conn_id)) + { + odbc_autocommit($this->conn_id, TRUE); + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + protected function _trans_rollback() + { + if (odbc_rollback($this->conn_id)) + { + odbc_autocommit($this->conn_id, TRUE); + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Platform-dependant string escape + * + * @param string + * @return string + */ + protected function _escape_str($str) + { + return remove_invisible_characters($str); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @return int + */ + public function affected_rows() + { + return odbc_num_rows($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @return bool + */ + public function insert_id() + { + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = '".$this->schema."'"; + + if ($prefix_limit !== FALSE && $this->dbprefix !== '') + { + return $sql." AND table_name LIKE '".$this->escape_like_str($this->dbprefix)."%' " + .sprintf($this->_like_escape_str, $this->_like_escape_chr); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @param string $table + * @return string + */ + protected function _list_columns($table = '') + { + return 'SHOW COLUMNS FROM '.$table; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @param string $table + * @return string + */ + protected function _field_data($table) + { + return 'SELECT TOP 1 FROM '.$table; + } + + // -------------------------------------------------------------------- + + /** + * Error + * + * Returns an array containing code and message of the last + * database error that has occured. + * + * @return array + */ + public function error() + { + return array('code' => odbc_error($this->conn_id), 'message' => odbc_errormsg($this->conn_id)); + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @param string $table + * @param array $values + * @return string + */ + protected function _update($table, $values) + { + $this->qb_limit = FALSE; + $this->qb_orderby = array(); + return parent::_update($table, $values); + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * + * If the database does not support the TRUNCATE statement, + * then this method maps to 'DELETE FROM table' + * + * @param string $table + * @return string + */ + protected function _truncate($table) + { + return 'DELETE FROM '.$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @param string $table + * @return string + */ + protected function _delete($table) + { + $this->qb_limit = FALSE; + return parent::_delete($table); + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @return void + */ + protected function _close() + { + odbc_close($this->conn_id); + } + +} diff --git a/codeigniter-3.0/system/database/drivers/odbc/odbc_forge.php b/ci-3.0/system/database/drivers/odbc/odbc_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/odbc/odbc_forge.php rename to ci-3.0/system/database/drivers/odbc/odbc_forge.php diff --git a/codeigniter-3.0/system/database/drivers/odbc/odbc_result.php b/ci-3.0/system/database/drivers/odbc/odbc_result.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/odbc/odbc_result.php rename to ci-3.0/system/database/drivers/odbc/odbc_result.php diff --git a/codeigniter-3.0/system/database/drivers/odbc/odbc_utility.php b/ci-3.0/system/database/drivers/odbc/odbc_utility.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/odbc/odbc_utility.php rename to ci-3.0/system/database/drivers/odbc/odbc_utility.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/index.html b/ci-3.0/system/database/drivers/pdo/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/index.html rename to ci-3.0/system/database/drivers/pdo/index.html diff --git a/ci-3.0/system/database/drivers/pdo/pdo_driver.php b/ci-3.0/system/database/drivers/pdo/pdo_driver.php new file mode 100755 index 000000000..8c5a5e7e3 --- /dev/null +++ b/ci-3.0/system/database/drivers/pdo/pdo_driver.php @@ -0,0 +1,372 @@ +dsn, $match) && count($match) === 2) + { + // If there is a minimum valid dsn string pattern found, we're done + // This is for general PDO users, who tend to have a full DSN string. + $this->subdriver = $match[1]; + return; + } + // Legacy support for DSN specified in the hostname field + elseif (preg_match('/([^:]+):/', $this->hostname, $match) && count($match) === 2) + { + $this->dsn = $this->hostname; + $this->hostname = NULL; + $this->subdriver = $match[1]; + return; + } + elseif (in_array($this->subdriver, array('mssql', 'sybase'), TRUE)) + { + $this->subdriver = 'dblib'; + } + elseif ($this->subdriver === '4D') + { + $this->subdriver = '4d'; + } + elseif ( ! in_array($this->subdriver, array('4d', 'cubrid', 'dblib', 'firebird', 'ibm', 'informix', 'mysql', 'oci', 'odbc', 'pgsql', 'sqlite', 'sqlsrv'), TRUE)) + { + log_message('error', 'PDO: Invalid or non-existent subdriver'); + + if ($this->db_debug) + { + show_error('Invalid or non-existent PDO subdriver'); + } + } + + $this->dsn = NULL; + } + + // -------------------------------------------------------------------- + + /** + * Database connection + * + * @param bool $persistent + * @return object + */ + public function db_connect($persistent = FALSE) + { + $this->options[PDO::ATTR_PERSISTENT] = $persistent; + + try + { + return new PDO($this->dsn, $this->username, $this->password, $this->options); + } + catch (PDOException $e) + { + if ($this->db_debug && empty($this->failover)) + { + $this->display_error($e->getMessage(), '', TRUE); + } + + return FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Database version number + * + * @return string + */ + public function version() + { + if (isset($this->data_cache['version'])) + { + return $this->data_cache['version']; + } + + // Not all subdrivers support the getAttribute() method + try + { + return $this->data_cache['version'] = $this->conn_id->getAttribute(PDO::ATTR_SERVER_VERSION); + } + catch (PDOException $e) + { + return parent::version(); + } + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @param string $sql SQL query + * @return mixed + */ + protected function _execute($sql) + { + return $this->conn_id->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + protected function _trans_begin() + { + return $this->conn_id->beginTransaction(); + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + protected function _trans_commit() + { + return $this->conn_id->commit(); + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + protected function _trans_rollback() + { + return $this->conn_id->rollBack(); + } + + // -------------------------------------------------------------------- + + /** + * Platform-dependant string escape + * + * @param string + * @return string + */ + protected function _escape_str($str) + { + // Escape the string + $str = $this->conn_id->quote($str); + + // If there are duplicated quotes, trim them away + return ($str[0] === "'") + ? substr($str, 1, -1) + : $str; + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @return int + */ + public function affected_rows() + { + return is_object($this->result_id) ? $this->result_id->rowCount() : 0; + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @param string $name + * @return int + */ + public function insert_id($name = NULL) + { + return $this->conn_id->lastInsertId($name); + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @param string $table + * @return string + */ + protected function _field_data($table) + { + return 'SELECT TOP 1 * FROM '.$this->protect_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * Error + * + * Returns an array containing code and message of the last + * database error that has occured. + * + * @return array + */ + public function error() + { + $error = array('code' => '00000', 'message' => ''); + $pdo_error = $this->conn_id->errorInfo(); + + if (empty($pdo_error[0])) + { + return $error; + } + + $error['code'] = isset($pdo_error[1]) ? $pdo_error[0].'/'.$pdo_error[1] : $pdo_error[0]; + if (isset($pdo_error[2])) + { + $error['message'] = $pdo_error[2]; + } + + return $error; + } + + // -------------------------------------------------------------------- + + /** + * Update_Batch statement + * + * Generates a platform-specific batch update string from the supplied data + * + * @param string $table Table name + * @param array $values Update data + * @param string $index WHERE key + * @return string + */ + protected function _update_batch($table, $values, $index) + { + $ids = array(); + foreach ($values as $key => $val) + { + $ids[] = $val[$index]; + + foreach (array_keys($val) as $field) + { + if ($field !== $index) + { + $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field]; + } + } + } + + $cases = ''; + foreach ($final as $k => $v) + { + $cases .= $k.' = CASE '."\n"; + + foreach ($v as $row) + { + $cases .= $row."\n"; + } + + $cases .= 'ELSE '.$k.' END, '; + } + + $this->where($index.' IN('.implode(',', $ids).')', NULL, FALSE); + + return 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where'); + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * + * If the database does not support the TRUNCATE statement, + * then this method maps to 'DELETE FROM table' + * + * @param string $table + * @return string + */ + protected function _truncate($table) + { + return 'TRUNCATE TABLE '.$table; + } + +} diff --git a/codeigniter-3.0/system/database/drivers/pdo/pdo_forge.php b/ci-3.0/system/database/drivers/pdo/pdo_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/pdo_forge.php rename to ci-3.0/system/database/drivers/pdo/pdo_forge.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/pdo_result.php b/ci-3.0/system/database/drivers/pdo/pdo_result.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/pdo_result.php rename to ci-3.0/system/database/drivers/pdo/pdo_result.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/pdo_utility.php b/ci-3.0/system/database/drivers/pdo/pdo_utility.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/pdo_utility.php rename to ci-3.0/system/database/drivers/pdo/pdo_utility.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/index.html b/ci-3.0/system/database/drivers/pdo/subdrivers/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/index.html rename to ci-3.0/system/database/drivers/pdo/subdrivers/index.html diff --git a/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php new file mode 100755 index 000000000..7a767ef40 --- /dev/null +++ b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php @@ -0,0 +1,200 @@ +dsn)) + { + $this->dsn = '4D:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname); + + empty($this->port) OR $this->dsn .= ';port='.$this->port; + empty($this->database) OR $this->dsn .= ';dbname='.$this->database; + empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set; + } + elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 3) === FALSE) + { + $this->dsn .= ';charset='.$this->char_set; + } + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = 'SELECT '.$this->escape_identifiers('TABLE_NAME').' FROM '.$this->escape_identifiers('_USER_TABLES'); + + if ($prefix_limit === TRUE && $this->dbprefix !== '') + { + $sql .= ' WHERE '.$this->escape_identifiers('TABLE_NAME')." LIKE '".$this->escape_like_str($this->dbprefix)."%' " + .sprintf($this->_like_escape_str, $this->_like_escape_chr); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @param string $table + * @return string + */ + protected function _list_columns($table = '') + { + return 'SELECT '.$this->escape_identifiers('COLUMN_NAME').' FROM '.$this->escape_identifiers('_USER_COLUMNS') + .' WHERE '.$this->escape_identifiers('TABLE_NAME').' = '.$this->escape($table); + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @param string $table + * @return string + */ + protected function _field_data($table) + { + return 'SELECT * FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE).' LIMIT 1'; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @param string $table + * @param array $values + * @return string + */ + protected function _update($table, $values) + { + $this->qb_limit = FALSE; + $this->qb_orderby = array(); + return parent::_update($table, $values); + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @param string $table + * @return string + */ + protected function _delete($table) + { + $this->qb_limit = FALSE; + return parent::_delete($table); + } + + // -------------------------------------------------------------------- + + /** + * LIMIT + * + * Generates a platform-specific LIMIT clause + * + * @param string $sql SQL Query + * @return string + */ + protected function _limit($sql) + { + return $sql.' LIMIT '.$this->qb_limit.($this->qb_offset ? ' OFFSET '.$this->qb_offset : ''); + } + +} diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_4d_forge.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_4d_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_4d_forge.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_4d_forge.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_cubrid_driver.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php diff --git a/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php new file mode 100755 index 000000000..ae2b9983b --- /dev/null +++ b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php @@ -0,0 +1,332 @@ +dsn)) + { + $this->dsn = $params['subdriver'].':host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname); + + if ( ! empty($this->port)) + { + $this->dsn .= (DIRECTORY_SEPARATOR === '\\' ? ',' : ':').$this->port; + } + + empty($this->database) OR $this->dsn .= ';dbname='.$this->database; + empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set; + empty($this->appname) OR $this->dsn .= ';appname='.$this->appname; + } + else + { + if ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 6) === FALSE) + { + $this->dsn .= ';charset='.$this->char_set; + } + + $this->subdriver = 'dblib'; + } + } + + // -------------------------------------------------------------------- + + /** + * Database connection + * + * @param bool $persistent + * @return object + */ + public function db_connect($persistent = FALSE) + { + $this->conn_id = parent::db_connect($persistent); + + if ( ! is_object($this->conn_id)) + { + return $this->conn_id; + } + + // Determine how identifiers are escaped + $query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi'); + $query = $query->row_array(); + $this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi']; + $this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']'); + + return $this->conn_id; + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = 'SELECT '.$this->escape_identifiers('name') + .' FROM '.$this->escape_identifiers('sysobjects') + .' WHERE '.$this->escape_identifiers('type')." = 'U'"; + + if ($prefix_limit === TRUE && $this->dbprefix !== '') + { + $sql .= ' AND '.$this->escape_identifiers('name')." LIKE '".$this->escape_like_str($this->dbprefix)."%' " + .sprintf($this->_like_escape_str, $this->_like_escape_chr); + } + + return $sql.' ORDER BY '.$this->escape_identifiers('name'); + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @param string $table + * @return string + */ + protected function _list_columns($table = '') + { + return 'SELECT COLUMN_NAME + FROM INFORMATION_SCHEMA.Columns + WHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table)); + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function field_data($table) + { + $sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, COLUMN_DEFAULT + FROM INFORMATION_SCHEMA.Columns + WHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table)); + + if (($query = $this->query($sql)) === FALSE) + { + return FALSE; + } + $query = $query->result_object(); + + $retval = array(); + for ($i = 0, $c = count($query); $i < $c; $i++) + { + $retval[$i] = new stdClass(); + $retval[$i]->name = $query[$i]->COLUMN_NAME; + $retval[$i]->type = $query[$i]->DATA_TYPE; + $retval[$i]->max_length = ($query[$i]->CHARACTER_MAXIMUM_LENGTH > 0) ? $query[$i]->CHARACTER_MAXIMUM_LENGTH : $query[$i]->NUMERIC_PRECISION; + $retval[$i]->default = $query[$i]->COLUMN_DEFAULT; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @param string $table + * @param array $values + * @return string + */ + protected function _update($table, $values) + { + $this->qb_limit = FALSE; + $this->qb_orderby = array(); + return parent::_update($table, $values); + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @param string $table + * @return string + */ + protected function _delete($table) + { + if ($this->qb_limit) + { + return 'WITH ci_delete AS (SELECT TOP '.$this->qb_limit.' * FROM '.$table.$this->_compile_wh('qb_where').') DELETE FROM ci_delete'; + } + + return parent::_delete($table); + } + + // -------------------------------------------------------------------- + + /** + * LIMIT + * + * Generates a platform-specific LIMIT clause + * + * @param string $sql SQL Query + * @return string + */ + protected function _limit($sql) + { + $limit = $this->qb_offset + $this->qb_limit; + + // As of SQL Server 2005 (9.0.*) ROW_NUMBER() is supported, + // however an ORDER BY clause is required for it to work + if (version_compare($this->version(), '9', '>=') && $this->qb_offset && ! empty($this->qb_orderby)) + { + $orderby = $this->_compile_order_by(); + + // We have to strip the ORDER BY clause + $sql = trim(substr($sql, 0, strrpos($sql, $orderby))); + + // Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results + if (count($this->qb_select) === 0) + { + $select = '*'; // Inevitable + } + else + { + // Use only field names and their aliases, everything else is out of our scope. + $select = array(); + $field_regexp = ($this->_quoted_identifier) + ? '("[^\"]+")' : '(\[[^\]]+\])'; + for ($i = 0, $c = count($this->qb_select); $i < $c; $i++) + { + $select[] = preg_match('/(?:\s|\.)'.$field_regexp.'$/i', $this->qb_select[$i], $m) + ? $m[1] : $this->qb_select[$i]; + } + $select = implode(', ', $select); + } + + return 'SELECT '.$select." FROM (\n\n" + .preg_replace('/^(SELECT( DISTINCT)?)/i', '\\1 ROW_NUMBER() OVER('.trim($orderby).') AS '.$this->escape_identifiers('CI_rownum').', ', $sql) + ."\n\n) ".$this->escape_identifiers('CI_subquery') + ."\nWHERE ".$this->escape_identifiers('CI_rownum').' BETWEEN '.($this->qb_offset + 1).' AND '.$limit; + } + + return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql); + } + + // -------------------------------------------------------------------- + + /** + * Insert batch statement + * + * Generates a platform-specific insert string from the supplied data. + * + * @param string $table Table name + * @param array $keys INSERT keys + * @param array $values INSERT values + * @return string|bool + */ + protected function _insert_batch($table, $keys, $values) + { + // Multiple-value inserts are only supported as of SQL Server 2008 + if (version_compare($this->version(), '10', '>=')) + { + return parent::_insert_batch($table, $keys, $values); + } + + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + +} diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php diff --git a/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php new file mode 100755 index 000000000..0bafde861 --- /dev/null +++ b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php @@ -0,0 +1,263 @@ +dsn)) + { + $this->dsn = 'firebird:'; + + if ( ! empty($this->database)) + { + $this->dsn .= 'dbname='.$this->database; + } + elseif ( ! empty($this->hostname)) + { + $this->dsn .= 'dbname='.$this->hostname; + } + + empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set; + empty($this->role) OR $this->dsn .= ';role='.$this->role; + } + elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 9) === FALSE) + { + $this->dsn .= ';charset='.$this->char_set; + } + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = 'SELECT "RDB$RELATION_NAME" FROM "RDB$RELATIONS" WHERE "RDB$RELATION_NAME" NOT LIKE \'RDB$%\' AND "RDB$RELATION_NAME" NOT LIKE \'MON$%\''; + + if ($prefix_limit === TRUE && $this->dbprefix !== '') + { + return $sql.' AND "RDB$RELATION_NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' " + .sprintf($this->_like_escape_str, $this->_like_escape_chr); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @param string $table + * @return string + */ + protected function _list_columns($table = '') + { + return 'SELECT "RDB$FIELD_NAME" FROM "RDB$RELATION_FIELDS" WHERE "RDB$RELATION_NAME" = '.$this->escape($table); + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function field_data($table) + { + $sql = 'SELECT "rfields"."RDB$FIELD_NAME" AS "name", + CASE "fields"."RDB$FIELD_TYPE" + WHEN 7 THEN \'SMALLINT\' + WHEN 8 THEN \'INTEGER\' + WHEN 9 THEN \'QUAD\' + WHEN 10 THEN \'FLOAT\' + WHEN 11 THEN \'DFLOAT\' + WHEN 12 THEN \'DATE\' + WHEN 13 THEN \'TIME\' + WHEN 14 THEN \'CHAR\' + WHEN 16 THEN \'INT64\' + WHEN 27 THEN \'DOUBLE\' + WHEN 35 THEN \'TIMESTAMP\' + WHEN 37 THEN \'VARCHAR\' + WHEN 40 THEN \'CSTRING\' + WHEN 261 THEN \'BLOB\' + ELSE NULL + END AS "type", + "fields"."RDB$FIELD_LENGTH" AS "max_length", + "rfields"."RDB$DEFAULT_VALUE" AS "default" + FROM "RDB$RELATION_FIELDS" "rfields" + JOIN "RDB$FIELDS" "fields" ON "rfields"."RDB$FIELD_SOURCE" = "fields"."RDB$FIELD_NAME" + WHERE "rfields"."RDB$RELATION_NAME" = '.$this->escape($table).' + ORDER BY "rfields"."RDB$FIELD_POSITION"'; + + return (($query = $this->query($sql)) !== FALSE) + ? $query->result_object() + : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @param string $table + * @param array $values + * @return string + */ + protected function _update($table, $values) + { + $this->qb_limit = FALSE; + return parent::_update($table, $values); + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * + * If the database does not support the TRUNCATE statement, + * then this method maps to 'DELETE FROM table' + * + * @param string $table + * @return string + */ + protected function _truncate($table) + { + return 'DELETE FROM '.$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @param string $table + * @return string + */ + protected function _delete($table) + { + $this->qb_limit = FALSE; + return parent::_delete($table); + } + + // -------------------------------------------------------------------- + + /** + * LIMIT + * + * Generates a platform-specific LIMIT clause + * + * @param string $sql SQL Query + * @return string + */ + protected function _limit($sql) + { + // Limit clause depends on if Interbase or Firebird + if (stripos($this->version(), 'firebird') !== FALSE) + { + $select = 'FIRST '.$this->qb_limit + .($this->qb_offset > 0 ? ' SKIP '.$this->qb_offset : ''); + } + else + { + $select = 'ROWS ' + .($this->qb_offset > 0 ? $this->qb_offset.' TO '.($this->qb_limit + $this->qb_offset) : $this->qb_limit); + } + + return preg_replace('`SELECT`i', 'SELECT '.$select, $sql); + } + +} diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_firebird_forge.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_firebird_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_firebird_forge.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_firebird_forge.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_ibm_driver.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_ibm_forge.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_ibm_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_ibm_forge.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_ibm_forge.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_informix_driver.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_informix_forge.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_informix_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_informix_forge.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_informix_forge.php diff --git a/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php new file mode 100755 index 000000000..e9d25cebc --- /dev/null +++ b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php @@ -0,0 +1,317 @@ +dsn)) + { + $this->dsn = 'mysql:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname); + + empty($this->port) OR $this->dsn .= ';port='.$this->port; + empty($this->database) OR $this->dsn .= ';dbname='.$this->database; + empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set; + } + elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 6) === FALSE && is_php('5.3.6')) + { + $this->dsn .= ';charset='.$this->char_set; + } + } + + // -------------------------------------------------------------------- + + /** + * Database connection + * + * @param bool $persistent + * @return object + */ + public function db_connect($persistent = FALSE) + { + /* Prior to PHP 5.3.6, even if the charset was supplied in the DSN + * on connect - it was ignored. This is a work-around for the issue. + * + * Reference: http://www.php.net/manual/en/ref.pdo-mysql.connection.php + */ + if ( ! is_php('5.3.6') && ! empty($this->char_set)) + { + $this->options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES '.$this->char_set + .(empty($this->dbcollat) ? '' : ' COLLATE '.$this->dbcollat); + } + + if ($this->stricton) + { + if (empty($this->options[PDO::MYSQL_ATTR_INIT_COMMAND])) + { + $this->options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET SESSION sql_mode="STRICT_ALL_TABLES"'; + } + else + { + $this->options[PDO::MYSQL_ATTR_INIT_COMMAND] .= ', @@session.sql_mode = "STRICT_ALL_TABLES"'; + } + } + + if ($this->compress === TRUE) + { + $this->options[PDO::MYSQL_ATTR_COMPRESS] = TRUE; + } + + // SSL support was added to PDO_MYSQL in PHP 5.3.7 + if (is_array($this->encrypt) && is_php('5.3.7')) + { + $ssl = array(); + empty($this->encrypt['ssl_key']) OR $ssl[PDO::MYSQL_ATTR_SSL_KEY] = $this->encrypt['ssl_key']; + empty($this->encrypt['ssl_cert']) OR $ssl[PDO::MYSQL_ATTR_SSL_CERT] = $this->encrypt['ssl_cert']; + empty($this->encrypt['ssl_ca']) OR $ssl[PDO::MYSQL_ATTR_SSL_CA] = $this->encrypt['ssl_ca']; + empty($this->encrypt['ssl_capath']) OR $ssl[PDO::MYSQL_ATTR_SSL_CAPATH] = $this->encrypt['ssl_capath']; + empty($this->encrypt['ssl_cipher']) OR $ssl[PDO::MYSQL_ATTR_SSL_CIPHER] = $this->encrypt['ssl_cipher']; + + // DO NOT use array_merge() here! + // It re-indexes numeric keys and the PDO_MYSQL_ATTR_SSL_* constants are integers. + empty($ssl) OR $this->options += $ssl; + } + + // Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails + if ( + ($pdo = parent::db_connect($persistent)) !== FALSE + && ! empty($ssl) + && version_compare($pdo->getAttribute(PDO::ATTR_CLIENT_VERSION), '5.7.3', '<=') + && empty($pdo->query("SHOW STATUS LIKE 'ssl_cipher'")->fetchObject()->Value) + ) + { + $message = 'PDO_MYSQL was configured for an SSL connection, but got an unencrypted connection instead!'; + log_message('error', $message); + return ($this->db->db_debug) ? $this->db->display_error($message, '', TRUE) : FALSE; + } + + return $pdo; + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @param string $database + * @return bool + */ + public function db_select($database = '') + { + if ($database === '') + { + $database = $this->database; + } + + if (FALSE !== $this->simple_query('USE '.$this->escape_identifiers($database))) + { + $this->database = $database; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = 'SHOW TABLES'; + + if ($prefix_limit === TRUE && $this->dbprefix !== '') + { + return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'"; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @param string $table + * @return string + */ + protected function _list_columns($table = '') + { + return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE); + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function field_data($table) + { + if (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE) + { + return FALSE; + } + $query = $query->result_object(); + + $retval = array(); + for ($i = 0, $c = count($query); $i < $c; $i++) + { + $retval[$i] = new stdClass(); + $retval[$i]->name = $query[$i]->Field; + + sscanf($query[$i]->Type, '%[a-z](%d)', + $retval[$i]->type, + $retval[$i]->max_length + ); + + $retval[$i]->default = $query[$i]->Default; + $retval[$i]->primary_key = (int) ($query[$i]->Key === 'PRI'); + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * + * If the database does not support the TRUNCATE statement, + * then this method maps to 'DELETE FROM table' + * + * @param string $table + * @return string + */ + protected function _truncate($table) + { + return 'TRUNCATE '.$table; + } + + // -------------------------------------------------------------------- + + /** + * FROM tables + * + * Groups tables in FROM clauses if needed, so there is no confusion + * about operator precedence. + * + * @return string + */ + protected function _from_tables() + { + if ( ! empty($this->qb_join) && count($this->qb_from) > 1) + { + return '('.implode(', ', $this->qb_from).')'; + } + + return implode(', ', $this->qb_from); + } + +} diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_mysql_forge.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_oci_driver.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_odbc_forge.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_odbc_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_odbc_forge.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_odbc_forge.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_pgsql_driver.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_pgsql_forge.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_pgsql_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_pgsql_forge.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_pgsql_forge.php diff --git a/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php new file mode 100755 index 000000000..409e6501b --- /dev/null +++ b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php @@ -0,0 +1,219 @@ +dsn)) + { + $this->dsn = 'sqlite:'; + + if (empty($this->database) && empty($this->hostname)) + { + $this->database = ':memory:'; + } + + $this->database = empty($this->database) ? $this->hostname : $this->database; + } + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = 'SELECT "NAME" FROM "SQLITE_MASTER" WHERE "TYPE" = \'table\''; + + if ($prefix_limit === TRUE && $this->dbprefix !== '') + { + return $sql.' AND "NAME" LIKE \''.$this->escape_like_str($this->dbprefix)."%' " + .sprintf($this->_like_escape_str, $this->_like_escape_chr); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * @param string $table Table name + * @return array + */ + public function list_fields($table) + { + // Is there a cached result? + if (isset($this->data_cache['field_names'][$table])) + { + return $this->data_cache['field_names'][$table]; + } + + if (($result = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE) + { + return FALSE; + } + + $this->data_cache['field_names'][$table] = array(); + foreach ($result->result_array() as $row) + { + $this->data_cache['field_names'][$table][] = $row['name']; + } + + return $this->data_cache['field_names'][$table]; + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function field_data($table) + { + if (($query = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE) + { + return FALSE; + } + + $query = $query->result_array(); + if (empty($query)) + { + return FALSE; + } + + $retval = array(); + for ($i = 0, $c = count($query); $i < $c; $i++) + { + $retval[$i] = new stdClass(); + $retval[$i]->name = $query[$i]['name']; + $retval[$i]->type = $query[$i]['type']; + $retval[$i]->max_length = NULL; + $retval[$i]->default = $query[$i]['dflt_value']; + $retval[$i]->primary_key = isset($query[$i]['pk']) ? (int) $query[$i]['pk'] : 0; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Replace statement + * + * @param string $table Table name + * @param array $keys INSERT keys + * @param array $values INSERT values + * @return string + */ + protected function _replace($table, $keys, $values) + { + return 'INSERT OR '.parent::_replace($table, $keys, $values); + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * + * If the database does not support the TRUNCATE statement, + * then this method maps to 'DELETE FROM table' + * + * @param string $table + * @return string + */ + protected function _truncate($table) + { + return 'DELETE FROM '.$table; + } + +} diff --git a/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php new file mode 100755 index 000000000..15afbdef5 --- /dev/null +++ b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php @@ -0,0 +1,238 @@ +db->version(), '3.3', '<')) + { + $this->_create_table_if = FALSE; + $this->_drop_table_if = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Create database + * + * @param string $db_name (ignored) + * @return bool + */ + public function create_database($db_name = '') + { + // In SQLite, a database is created when you connect to the database. + // We'll return TRUE so that an error isn't generated + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @param string $db_name (ignored) + * @return bool + */ + public function drop_database($db_name = '') + { + // In SQLite, a database is dropped when we delete a file + if (file_exists($this->db->database)) + { + // We need to close the pseudo-connection first + $this->db->close(); + if ( ! @unlink($this->db->database)) + { + return $this->db->db_debug ? $this->db->display_error('db_unable_to_drop') : FALSE; + } + elseif ( ! empty($this->db->data_cache['db_names'])) + { + $key = array_search(strtolower($this->db->database), array_map('strtolower', $this->db->data_cache['db_names']), TRUE); + if ($key !== FALSE) + { + unset($this->db->data_cache['db_names'][$key]); + } + } + + return TRUE; + } + + return $this->db->db_debug ? $this->db->display_error('db_unable_to_drop') : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * ALTER TABLE + * + * @param string $alter_type ALTER type + * @param string $table Table name + * @param mixed $field Column definition + * @return string|string[] + */ + protected function _alter_table($alter_type, $table, $field) + { + if ($alter_type === 'DROP' OR $alter_type === 'CHANGE') + { + // drop_column(): + // BEGIN TRANSACTION; + // CREATE TEMPORARY TABLE t1_backup(a,b); + // INSERT INTO t1_backup SELECT a,b FROM t1; + // DROP TABLE t1; + // CREATE TABLE t1(a,b); + // INSERT INTO t1 SELECT a,b FROM t1_backup; + // DROP TABLE t1_backup; + // COMMIT; + + return FALSE; + } + + return parent::_alter_table($alter_type, $table, $field); + } + + // -------------------------------------------------------------------- + + /** + * Process column + * + * @param array $field + * @return string + */ + protected function _process_column($field) + { + return $this->db->escape_identifiers($field['name']) + .' '.$field['type'] + .$field['auto_increment'] + .$field['null'] + .$field['unique'] + .$field['default']; + } + + // -------------------------------------------------------------------- + + /** + * Field attribute TYPE + * + * Performs a data type mapping between different databases. + * + * @param array &$attributes + * @return void + */ + protected function _attr_type(&$attributes) + { + switch (strtoupper($attributes['TYPE'])) + { + case 'ENUM': + case 'SET': + $attributes['TYPE'] = 'TEXT'; + return; + default: return; + } + } + + // -------------------------------------------------------------------- + + /** + * Field attribute AUTO_INCREMENT + * + * @param array &$attributes + * @param array &$field + * @return void + */ + protected function _attr_auto_increment(&$attributes, &$field) + { + if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE) + { + $field['type'] = 'INTEGER PRIMARY KEY'; + $field['default'] = ''; + $field['null'] = ''; + $field['unique'] = ''; + $field['auto_increment'] = ' AUTOINCREMENT'; + + $this->primary_keys = array(); + } + } + +} diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php diff --git a/codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php b/ci-3.0/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php rename to ci-3.0/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php diff --git a/codeigniter-3.0/system/database/drivers/postgre/index.html b/ci-3.0/system/database/drivers/postgre/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/postgre/index.html rename to ci-3.0/system/database/drivers/postgre/index.html diff --git a/ci-3.0/system/database/drivers/postgre/postgre_driver.php b/ci-3.0/system/database/drivers/postgre/postgre_driver.php new file mode 100755 index 000000000..b1df326f7 --- /dev/null +++ b/ci-3.0/system/database/drivers/postgre/postgre_driver.php @@ -0,0 +1,615 @@ +dsn)) + { + return; + } + + $this->dsn === '' OR $this->dsn = ''; + + if (strpos($this->hostname, '/') !== FALSE) + { + // If UNIX sockets are used, we shouldn't set a port + $this->port = ''; + } + + $this->hostname === '' OR $this->dsn = 'host='.$this->hostname.' '; + + if ( ! empty($this->port) && ctype_digit($this->port)) + { + $this->dsn .= 'port='.$this->port.' '; + } + + if ($this->username !== '') + { + $this->dsn .= 'user='.$this->username.' '; + + /* An empty password is valid! + * + * $db['password'] = NULL must be done in order to ignore it. + */ + $this->password === NULL OR $this->dsn .= "password='".$this->password."' "; + } + + $this->database === '' OR $this->dsn .= 'dbname='.$this->database.' '; + + /* We don't have these options as elements in our standard configuration + * array, but they might be set by parse_url() if the configuration was + * provided via string. Example: + * + * postgre://username:password@localhost:5432/database?connect_timeout=5&sslmode=1 + */ + foreach (array('connect_timeout', 'options', 'sslmode', 'service') as $key) + { + if (isset($this->$key) && is_string($this->key) && $this->key !== '') + { + $this->dsn .= $key."='".$this->key."' "; + } + } + + $this->dsn = rtrim($this->dsn); + } + + // -------------------------------------------------------------------- + + /** + * Database connection + * + * @param bool $persistent + * @return resource + */ + public function db_connect($persistent = FALSE) + { + $this->conn_id = ($persistent === TRUE) + ? pg_pconnect($this->dsn) + : pg_connect($this->dsn); + + if ($this->conn_id !== FALSE) + { + if ($persistent === TRUE + && pg_connection_status($this->conn_id) === PGSQL_CONNECTION_BAD + && pg_ping($this->conn_id) === FALSE + ) + { + return FALSE; + } + + empty($this->schema) OR $this->simple_query('SET search_path TO '.$this->schema.',public'); + } + + return $this->conn_id; + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout + * + * @return void + */ + public function reconnect() + { + if (pg_ping($this->conn_id) === FALSE) + { + $this->conn_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @param string $charset + * @return bool + */ + protected function _db_set_charset($charset) + { + return (pg_set_client_encoding($this->conn_id, $charset) === 0); + } + + // -------------------------------------------------------------------- + + /** + * Database version number + * + * @return string + */ + public function version() + { + if (isset($this->data_cache['version'])) + { + return $this->data_cache['version']; + } + + if ( ! $this->conn_id OR ($pg_version = pg_version($this->conn_id)) === FALSE) + { + return FALSE; + } + + /* If PHP was compiled with PostgreSQL lib versions earlier + * than 7.4, pg_version() won't return the server version + * and so we'll have to fall back to running a query in + * order to get it. + */ + return isset($pg_version['server']) + ? $this->data_cache['version'] = $pg_version['server'] + : parent::version(); + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @param string $sql an SQL query + * @return resource + */ + protected function _execute($sql) + { + return pg_query($this->conn_id, $sql); + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + protected function _trans_begin() + { + return (bool) pg_query($this->conn_id, 'BEGIN'); + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + protected function _trans_commit() + { + return (bool) pg_query($this->conn_id, 'COMMIT'); + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + protected function _trans_rollback() + { + return (bool) pg_query($this->conn_id, 'ROLLBACK'); + } + + // -------------------------------------------------------------------- + + /** + * Determines if a query is a "write" type. + * + * @param string An SQL query string + * @return bool + */ + public function is_write_type($sql) + { + return (bool) preg_match('/^\s*"?(SET|INSERT(?![^\)]+\)\s+RETURNING)|UPDATE(?!.*\sRETURNING)|DELETE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX)\s/i', str_replace(array("\r\n", "\r", "\n"), ' ', $sql)); + } + + // -------------------------------------------------------------------- + + /** + * Platform-dependant string escape + * + * @param string + * @return string + */ + protected function _escape_str($str) + { + return pg_escape_string($this->conn_id, $str); + } + + // -------------------------------------------------------------------- + + /** + * "Smart" Escape String + * + * Escapes data based on type + * + * @param string $str + * @return mixed + */ + public function escape($str) + { + if (is_php('5.4.4') && (is_string($str) OR (is_object($str) && method_exists($str, '__toString')))) + { + return pg_escape_literal($this->conn_id, $str); + } + elseif (is_bool($str)) + { + return ($str) ? 'TRUE' : 'FALSE'; + } + + return parent::escape($str); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @return int + */ + public function affected_rows() + { + return pg_affected_rows($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @return string + */ + public function insert_id() + { + $v = pg_version($this->conn_id); + $v = isset($v['server']) ? $v['server'] : 0; // 'server' key is only available since PosgreSQL 7.4 + + $table = (func_num_args() > 0) ? func_get_arg(0) : NULL; + $column = (func_num_args() > 1) ? func_get_arg(1) : NULL; + + if ($table === NULL && $v >= '8.1') + { + $sql = 'SELECT LASTVAL() AS ins_id'; + } + elseif ($table !== NULL) + { + if ($column !== NULL && $v >= '8.0') + { + $sql = 'SELECT pg_get_serial_sequence(\''.$table."', '".$column."') AS seq"; + $query = $this->query($sql); + $query = $query->row(); + $seq = $query->seq; + } + else + { + // seq_name passed in table parameter + $seq = $table; + } + + $sql = 'SELECT CURRVAL(\''.$seq."') AS ins_id"; + } + else + { + return pg_last_oid($this->result_id); + } + + $query = $this->query($sql); + $query = $query->row(); + return (int) $query->ins_id; + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = 'SELECT "table_name" FROM "information_schema"."tables" WHERE "table_schema" = \''.$this->schema."'"; + + if ($prefix_limit !== FALSE && $this->dbprefix !== '') + { + return $sql.' AND "table_name" LIKE \'' + .$this->escape_like_str($this->dbprefix)."%' " + .sprintf($this->_like_escape_str, $this->_like_escape_chr); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * List column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @param string $table + * @return string + */ + protected function _list_columns($table = '') + { + return 'SELECT "column_name" + FROM "information_schema"."columns" + WHERE LOWER("table_name") = '.$this->escape(strtolower($table)); + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function field_data($table) + { + $sql = 'SELECT "column_name", "data_type", "character_maximum_length", "numeric_precision", "column_default" + FROM "information_schema"."columns" + WHERE LOWER("table_name") = '.$this->escape(strtolower($table)); + + if (($query = $this->query($sql)) === FALSE) + { + return FALSE; + } + $query = $query->result_object(); + + $retval = array(); + for ($i = 0, $c = count($query); $i < $c; $i++) + { + $retval[$i] = new stdClass(); + $retval[$i]->name = $query[$i]->column_name; + $retval[$i]->type = $query[$i]->data_type; + $retval[$i]->max_length = ($query[$i]->character_maximum_length > 0) ? $query[$i]->character_maximum_length : $query[$i]->numeric_precision; + $retval[$i]->default = $query[$i]->column_default; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Error + * + * Returns an array containing code and message of the last + * database error that has occured. + * + * @return array + */ + public function error() + { + return array('code' => '', 'message' => pg_last_error($this->conn_id)); + } + + // -------------------------------------------------------------------- + + /** + * ORDER BY + * + * @param string $orderby + * @param string $direction ASC, DESC or RANDOM + * @param bool $escape + * @return object + */ + public function order_by($orderby, $direction = '', $escape = NULL) + { + $direction = strtoupper(trim($direction)); + if ($direction === 'RANDOM') + { + if ( ! is_float($orderby) && ctype_digit((string) $orderby)) + { + $orderby = ($orderby > 1) + ? (float) '0.'.$orderby + : (float) $orderby; + } + + if (is_float($orderby)) + { + $this->simple_query('SET SEED '.$orderby); + } + + $orderby = $this->_random_keyword[0]; + $direction = ''; + $escape = FALSE; + } + + return parent::order_by($orderby, $direction, $escape); + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @param string $table + * @param array $values + * @return string + */ + protected function _update($table, $values) + { + $this->qb_limit = FALSE; + $this->qb_orderby = array(); + return parent::_update($table, $values); + } + + // -------------------------------------------------------------------- + + /** + * Update_Batch statement + * + * Generates a platform-specific batch update string from the supplied data + * + * @param string $table Table name + * @param array $values Update data + * @param string $index WHERE key + * @return string + */ + protected function _update_batch($table, $values, $index) + { + $ids = array(); + foreach ($values as $key => $val) + { + $ids[] = $val[$index]; + + foreach (array_keys($val) as $field) + { + if ($field !== $index) + { + $final[$field][] = 'WHEN '.$val[$index].' THEN '.$val[$field]; + } + } + } + + $cases = ''; + foreach ($final as $k => $v) + { + $cases .= $k.' = (CASE '.$index."\n" + .implode("\n", $v)."\n" + .'ELSE '.$k.' END), '; + } + + $this->where($index.' IN('.implode(',', $ids).')', NULL, FALSE); + + return 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where'); + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @param string $table + * @return string + */ + protected function _delete($table) + { + $this->qb_limit = FALSE; + return parent::_delete($table); + } + + // -------------------------------------------------------------------- + + /** + * LIMIT + * + * Generates a platform-specific LIMIT clause + * + * @param string $sql SQL Query + * @return string + */ + protected function _limit($sql) + { + return $sql.' LIMIT '.$this->qb_limit.($this->qb_offset ? ' OFFSET '.$this->qb_offset : ''); + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @return void + */ + protected function _close() + { + pg_close($this->conn_id); + } + +} diff --git a/codeigniter-3.0/system/database/drivers/postgre/postgre_forge.php b/ci-3.0/system/database/drivers/postgre/postgre_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/postgre/postgre_forge.php rename to ci-3.0/system/database/drivers/postgre/postgre_forge.php diff --git a/codeigniter-3.0/system/database/drivers/postgre/postgre_result.php b/ci-3.0/system/database/drivers/postgre/postgre_result.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/postgre/postgre_result.php rename to ci-3.0/system/database/drivers/postgre/postgre_result.php diff --git a/codeigniter-3.0/system/database/drivers/postgre/postgre_utility.php b/ci-3.0/system/database/drivers/postgre/postgre_utility.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/postgre/postgre_utility.php rename to ci-3.0/system/database/drivers/postgre/postgre_utility.php diff --git a/codeigniter-3.0/system/database/drivers/sqlite/index.html b/ci-3.0/system/database/drivers/sqlite/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/sqlite/index.html rename to ci-3.0/system/database/drivers/sqlite/index.html diff --git a/ci-3.0/system/database/drivers/sqlite/sqlite_driver.php b/ci-3.0/system/database/drivers/sqlite/sqlite_driver.php new file mode 100755 index 000000000..e000a8e50 --- /dev/null +++ b/ci-3.0/system/database/drivers/sqlite/sqlite_driver.php @@ -0,0 +1,330 @@ +database, 0666, $error) + : sqlite_open($this->database, 0666, $error); + + isset($error) && log_message('error', $error); + + return $conn_id; + } + + // -------------------------------------------------------------------- + + /** + * Database version number + * + * @return string + */ + public function version() + { + return isset($this->data_cache['version']) + ? $this->data_cache['version'] + : $this->data_cache['version'] = sqlite_libversion(); + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @param string $sql an SQL query + * @return resource + */ + protected function _execute($sql) + { + return $this->is_write_type($sql) + ? sqlite_exec($this->conn_id, $sql) + : sqlite_query($this->conn_id, $sql); + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + protected function _trans_begin() + { + return $this->simple_query('BEGIN TRANSACTION'); + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + protected function _trans_commit() + { + return $this->simple_query('COMMIT'); + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + protected function _trans_rollback() + { + return $this->simple_query('ROLLBACK'); + } + + // -------------------------------------------------------------------- + + /** + * Platform-dependant string escape + * + * @param string + * @return string + */ + protected function _escape_str($str) + { + return sqlite_escape_string($str); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @return int + */ + public function affected_rows() + { + return sqlite_changes($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @return int + */ + public function insert_id() + { + return sqlite_last_insert_rowid($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = "SELECT name FROM sqlite_master WHERE type='table'"; + + if ($prefix_limit !== FALSE && $this->dbprefix != '') + { + return $sql." AND 'name' LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @param string $table + * @return bool + */ + protected function _list_columns($table = '') + { + // Not supported + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function field_data($table) + { + if (($query = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE) + { + return FALSE; + } + + $query = $query->result_array(); + if (empty($query)) + { + return FALSE; + } + + $retval = array(); + for ($i = 0, $c = count($query); $i < $c; $i++) + { + $retval[$i] = new stdClass(); + $retval[$i]->name = $query[$i]['name']; + $retval[$i]->type = $query[$i]['type']; + $retval[$i]->max_length = NULL; + $retval[$i]->default = $query[$i]['dflt_value']; + $retval[$i]->primary_key = isset($query[$i]['pk']) ? (int) $query[$i]['pk'] : 0; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Error + * + * Returns an array containing code and message of the last + * database error that has occured. + * + * @return array + */ + public function error() + { + $error = array('code' => sqlite_last_error($this->conn_id)); + $error['message'] = sqlite_error_string($error['code']); + return $error; + } + + // -------------------------------------------------------------------- + + /** + * Replace statement + * + * Generates a platform-specific replace string from the supplied data + * + * @param string $table Table name + * @param array $keys INSERT keys + * @param array $values INSERT values + * @return string + */ + protected function _replace($table, $keys, $values) + { + return 'INSERT OR '.parent::_replace($table, $keys, $values); + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * + * If the database does not support the TRUNCATE statement, + * then this function maps to 'DELETE FROM table' + * + * @param string $table + * @return string + */ + protected function _truncate($table) + { + return 'DELETE FROM '.$table; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @return void + */ + protected function _close() + { + sqlite_close($this->conn_id); + } + +} diff --git a/codeigniter-3.0/system/database/drivers/sqlite/sqlite_forge.php b/ci-3.0/system/database/drivers/sqlite/sqlite_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/sqlite/sqlite_forge.php rename to ci-3.0/system/database/drivers/sqlite/sqlite_forge.php diff --git a/codeigniter-3.0/system/database/drivers/sqlite/sqlite_result.php b/ci-3.0/system/database/drivers/sqlite/sqlite_result.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/sqlite/sqlite_result.php rename to ci-3.0/system/database/drivers/sqlite/sqlite_result.php diff --git a/codeigniter-3.0/system/database/drivers/sqlite/sqlite_utility.php b/ci-3.0/system/database/drivers/sqlite/sqlite_utility.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/sqlite/sqlite_utility.php rename to ci-3.0/system/database/drivers/sqlite/sqlite_utility.php diff --git a/codeigniter-3.0/system/database/drivers/sqlite3/index.html b/ci-3.0/system/database/drivers/sqlite3/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/sqlite3/index.html rename to ci-3.0/system/database/drivers/sqlite3/index.html diff --git a/ci-3.0/system/database/drivers/sqlite3/sqlite3_driver.php b/ci-3.0/system/database/drivers/sqlite3/sqlite3_driver.php new file mode 100755 index 000000000..73e453785 --- /dev/null +++ b/ci-3.0/system/database/drivers/sqlite3/sqlite3_driver.php @@ -0,0 +1,350 @@ +password) + ? new SQLite3($this->database) + : new SQLite3($this->database, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, $this->password); + } + catch (Exception $e) + { + return FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Database version number + * + * @return string + */ + public function version() + { + if (isset($this->data_cache['version'])) + { + return $this->data_cache['version']; + } + + $version = SQLite3::version(); + return $this->data_cache['version'] = $version['versionString']; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @todo Implement use of SQLite3::querySingle(), if needed + * @param string $sql + * @return mixed SQLite3Result object or bool + */ + protected function _execute($sql) + { + return $this->is_write_type($sql) + ? $this->conn_id->exec($sql) + : $this->conn_id->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + protected function _trans_begin() + { + return $this->conn_id->exec('BEGIN TRANSACTION'); + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + protected function _trans_commit() + { + return $this->conn_id->exec('END TRANSACTION'); + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + protected function _trans_rollback() + { + return $this->conn_id->exec('ROLLBACK'); + } + + // -------------------------------------------------------------------- + + /** + * Platform-dependant string escape + * + * @param string + * @return string + */ + protected function _escape_str($str) + { + return $this->conn_id->escapeString($str); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @return int + */ + public function affected_rows() + { + return $this->conn_id->changes(); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @return int + */ + public function insert_id() + { + return $this->conn_id->lastInsertRowID(); + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool $prefix_limit + * @return string + */ + protected function _list_tables($prefix_limit = FALSE) + { + return 'SELECT "NAME" FROM "SQLITE_MASTER" WHERE "TYPE" = \'table\'' + .(($prefix_limit !== FALSE && $this->dbprefix != '') + ? ' AND "NAME" LIKE \''.$this->escape_like_str($this->dbprefix).'%\' '.sprintf($this->_like_escape_str, $this->_like_escape_chr) + : ''); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * @param string $table Table name + * @return array + */ + public function list_fields($table) + { + // Is there a cached result? + if (isset($this->data_cache['field_names'][$table])) + { + return $this->data_cache['field_names'][$table]; + } + + if (($result = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE) + { + return FALSE; + } + + $this->data_cache['field_names'][$table] = array(); + foreach ($result->result_array() as $row) + { + $this->data_cache['field_names'][$table][] = $row['name']; + } + + return $this->data_cache['field_names'][$table]; + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function field_data($table) + { + if (($query = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE) + { + return FALSE; + } + + $query = $query->result_array(); + if (empty($query)) + { + return FALSE; + } + + $retval = array(); + for ($i = 0, $c = count($query); $i < $c; $i++) + { + $retval[$i] = new stdClass(); + $retval[$i]->name = $query[$i]['name']; + $retval[$i]->type = $query[$i]['type']; + $retval[$i]->max_length = NULL; + $retval[$i]->default = $query[$i]['dflt_value']; + $retval[$i]->primary_key = isset($query[$i]['pk']) ? (int) $query[$i]['pk'] : 0; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Error + * + * Returns an array containing code and message of the last + * database error that has occured. + * + * @return array + */ + public function error() + { + return array('code' => $this->conn_id->lastErrorCode(), 'message' => $this->conn_id->lastErrorMsg()); + } + + // -------------------------------------------------------------------- + + /** + * Replace statement + * + * Generates a platform-specific replace string from the supplied data + * + * @param string $table Table name + * @param array $keys INSERT keys + * @param array $values INSERT values + * @return string + */ + protected function _replace($table, $keys, $values) + { + return 'INSERT OR '.parent::_replace($table, $keys, $values); + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * + * If the database does not support the TRUNCATE statement, + * then this method maps to 'DELETE FROM table' + * + * @param string $table + * @return string + */ + protected function _truncate($table) + { + return 'DELETE FROM '.$table; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @return void + */ + protected function _close() + { + $this->conn_id->close(); + } + +} diff --git a/ci-3.0/system/database/drivers/sqlite3/sqlite3_forge.php b/ci-3.0/system/database/drivers/sqlite3/sqlite3_forge.php new file mode 100755 index 000000000..24690ba20 --- /dev/null +++ b/ci-3.0/system/database/drivers/sqlite3/sqlite3_forge.php @@ -0,0 +1,225 @@ +db->version(), '3.3', '<')) + { + $this->_create_table_if = FALSE; + $this->_drop_table_if = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Create database + * + * @param string $db_name + * @return bool + */ + public function create_database($db_name = '') + { + // In SQLite, a database is created when you connect to the database. + // We'll return TRUE so that an error isn't generated + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @param string $db_name (ignored) + * @return bool + */ + public function drop_database($db_name = '') + { + // In SQLite, a database is dropped when we delete a file + if (file_exists($this->db->database)) + { + // We need to close the pseudo-connection first + $this->db->close(); + if ( ! @unlink($this->db->database)) + { + return $this->db->db_debug ? $this->db->display_error('db_unable_to_drop') : FALSE; + } + elseif ( ! empty($this->db->data_cache['db_names'])) + { + $key = array_search(strtolower($this->db->database), array_map('strtolower', $this->db->data_cache['db_names']), TRUE); + if ($key !== FALSE) + { + unset($this->db->data_cache['db_names'][$key]); + } + } + + return TRUE; + } + + return $this->db->db_debug ? $this->db->display_error('db_unable_to_drop') : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * ALTER TABLE + * + * @todo implement drop_column(), modify_column() + * @param string $alter_type ALTER type + * @param string $table Table name + * @param mixed $field Column definition + * @return string|string[] + */ + protected function _alter_table($alter_type, $table, $field) + { + if ($alter_type === 'DROP' OR $alter_type === 'CHANGE') + { + // drop_column(): + // BEGIN TRANSACTION; + // CREATE TEMPORARY TABLE t1_backup(a,b); + // INSERT INTO t1_backup SELECT a,b FROM t1; + // DROP TABLE t1; + // CREATE TABLE t1(a,b); + // INSERT INTO t1 SELECT a,b FROM t1_backup; + // DROP TABLE t1_backup; + // COMMIT; + + return FALSE; + } + + return parent::_alter_table($alter_type, $table, $field); + } + + // -------------------------------------------------------------------- + + /** + * Process column + * + * @param array $field + * @return string + */ + protected function _process_column($field) + { + return $this->db->escape_identifiers($field['name']) + .' '.$field['type'] + .$field['auto_increment'] + .$field['null'] + .$field['unique'] + .$field['default']; + } + + // -------------------------------------------------------------------- + + /** + * Field attribute TYPE + * + * Performs a data type mapping between different databases. + * + * @param array &$attributes + * @return void + */ + protected function _attr_type(&$attributes) + { + switch (strtoupper($attributes['TYPE'])) + { + case 'ENUM': + case 'SET': + $attributes['TYPE'] = 'TEXT'; + return; + default: return; + } + } + + // -------------------------------------------------------------------- + + /** + * Field attribute AUTO_INCREMENT + * + * @param array &$attributes + * @param array &$field + * @return void + */ + protected function _attr_auto_increment(&$attributes, &$field) + { + if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE) + { + $field['type'] = 'INTEGER PRIMARY KEY'; + $field['default'] = ''; + $field['null'] = ''; + $field['unique'] = ''; + $field['auto_increment'] = ' AUTOINCREMENT'; + + $this->primary_keys = array(); + } + } + +} diff --git a/codeigniter-3.0/system/database/drivers/sqlite3/sqlite3_result.php b/ci-3.0/system/database/drivers/sqlite3/sqlite3_result.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/sqlite3/sqlite3_result.php rename to ci-3.0/system/database/drivers/sqlite3/sqlite3_result.php diff --git a/codeigniter-3.0/system/database/drivers/sqlite3/sqlite3_utility.php b/ci-3.0/system/database/drivers/sqlite3/sqlite3_utility.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/sqlite3/sqlite3_utility.php rename to ci-3.0/system/database/drivers/sqlite3/sqlite3_utility.php diff --git a/codeigniter-3.0/system/database/drivers/sqlsrv/index.html b/ci-3.0/system/database/drivers/sqlsrv/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/sqlsrv/index.html rename to ci-3.0/system/database/drivers/sqlsrv/index.html diff --git a/ci-3.0/system/database/drivers/sqlsrv/sqlsrv_driver.php b/ci-3.0/system/database/drivers/sqlsrv/sqlsrv_driver.php new file mode 100755 index 000000000..414669a4b --- /dev/null +++ b/ci-3.0/system/database/drivers/sqlsrv/sqlsrv_driver.php @@ -0,0 +1,542 @@ +scrollable === NULL) + { + $this->scrollable = defined('SQLSRV_CURSOR_CLIENT_BUFFERED') + ? SQLSRV_CURSOR_CLIENT_BUFFERED + : FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Database connection + * + * @param bool $pooling + * @return resource + */ + public function db_connect($pooling = FALSE) + { + $charset = in_array(strtolower($this->char_set), array('utf-8', 'utf8'), TRUE) + ? 'UTF-8' : SQLSRV_ENC_CHAR; + + $connection = array( + 'UID' => empty($this->username) ? '' : $this->username, + 'PWD' => empty($this->password) ? '' : $this->password, + 'Database' => $this->database, + 'ConnectionPooling' => ($pooling === TRUE) ? 1 : 0, + 'CharacterSet' => $charset, + 'Encrypt' => ($this->encrypt === TRUE) ? 1 : 0, + 'ReturnDatesAsStrings' => 1 + ); + + // If the username and password are both empty, assume this is a + // 'Windows Authentication Mode' connection. + if (empty($connection['UID']) && empty($connection['PWD'])) + { + unset($connection['UID'], $connection['PWD']); + } + + if (FALSE !== ($this->conn_id = sqlsrv_connect($this->hostname, $connection))) + { + // Determine how identifiers are escaped + $query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi'); + $query = $query->row_array(); + $this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi']; + $this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']'); + } + + return $this->conn_id; + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @param string $database + * @return bool + */ + public function db_select($database = '') + { + if ($database === '') + { + $database = $this->database; + } + + if ($this->_execute('USE '.$this->escape_identifiers($database))) + { + $this->database = $database; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @param string $sql an SQL query + * @return resource + */ + protected function _execute($sql) + { + return ($this->scrollable === FALSE OR $this->is_write_type($sql)) + ? sqlsrv_query($this->conn_id, $sql) + : sqlsrv_query($this->conn_id, $sql, NULL, array('Scrollable' => $this->scrollable)); + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + protected function _trans_begin() + { + return sqlsrv_begin_transaction($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + protected function _trans_commit() + { + return sqlsrv_commit($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + protected function _trans_rollback() + { + return sqlsrv_rollback($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @return int + */ + public function affected_rows() + { + return sqlsrv_rows_affected($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * Returns the last id created in the Identity column. + * + * @return string + */ + public function insert_id() + { + return $this->query('SELECT SCOPE_IDENTITY() AS insert_id')->row()->insert_id; + } + + // -------------------------------------------------------------------- + + /** + * Database version number + * + * @return string + */ + public function version() + { + if (isset($this->data_cache['version'])) + { + return $this->data_cache['version']; + } + + if ( ! $this->conn_id OR ($info = sqlsrv_server_info($this->conn_id)) === FALSE) + { + return FALSE; + } + + return $this->data_cache['version'] = $info['SQLServerVersion']; + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @param bool + * @return string $prefix_limit + */ + protected function _list_tables($prefix_limit = FALSE) + { + $sql = 'SELECT '.$this->escape_identifiers('name') + .' FROM '.$this->escape_identifiers('sysobjects') + .' WHERE '.$this->escape_identifiers('type')." = 'U'"; + + if ($prefix_limit === TRUE && $this->dbprefix !== '') + { + $sql .= ' AND '.$this->escape_identifiers('name')." LIKE '".$this->escape_like_str($this->dbprefix)."%' " + .sprintf($this->_escape_like_str, $this->_escape_like_chr); + } + + return $sql.' ORDER BY '.$this->escape_identifiers('name'); + } + + // -------------------------------------------------------------------- + + /** + * List column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @param string $table + * @return string + */ + protected function _list_columns($table = '') + { + return 'SELECT COLUMN_NAME + FROM INFORMATION_SCHEMA.Columns + WHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table)); + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function field_data($table) + { + $sql = 'SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, COLUMN_DEFAULT + FROM INFORMATION_SCHEMA.Columns + WHERE UPPER(TABLE_NAME) = '.$this->escape(strtoupper($table)); + + if (($query = $this->query($sql)) === FALSE) + { + return FALSE; + } + $query = $query->result_object(); + + $retval = array(); + for ($i = 0, $c = count($query); $i < $c; $i++) + { + $retval[$i] = new stdClass(); + $retval[$i]->name = $query[$i]->COLUMN_NAME; + $retval[$i]->type = $query[$i]->DATA_TYPE; + $retval[$i]->max_length = ($query[$i]->CHARACTER_MAXIMUM_LENGTH > 0) ? $query[$i]->CHARACTER_MAXIMUM_LENGTH : $query[$i]->NUMERIC_PRECISION; + $retval[$i]->default = $query[$i]->COLUMN_DEFAULT; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Error + * + * Returns an array containing code and message of the last + * database error that has occured. + * + * @return array + */ + public function error() + { + $error = array('code' => '00000', 'message' => ''); + $sqlsrv_errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); + + if ( ! is_array($sqlsrv_errors)) + { + return $error; + } + + $sqlsrv_error = array_shift($sqlsrv_errors); + if (isset($sqlsrv_error['SQLSTATE'])) + { + $error['code'] = isset($sqlsrv_error['code']) ? $sqlsrv_error['SQLSTATE'].'/'.$sqlsrv_error['code'] : $sqlsrv_error['SQLSTATE']; + } + elseif (isset($sqlsrv_error['code'])) + { + $error['code'] = $sqlsrv_error['code']; + } + + if (isset($sqlsrv_error['message'])) + { + $error['message'] = $sqlsrv_error['message']; + } + + return $error; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @param string $table + * @param array $values + * @return string + */ + protected function _update($table, $values) + { + $this->qb_limit = FALSE; + $this->qb_orderby = array(); + return parent::_update($table, $values); + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * + * If the database does not support the TRUNCATE statement, + * then this method maps to 'DELETE FROM table' + * + * @param string $table + * @return string + */ + protected function _truncate($table) + { + return 'TRUNCATE TABLE '.$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @param string $table + * @return string + */ + protected function _delete($table) + { + if ($this->qb_limit) + { + return 'WITH ci_delete AS (SELECT TOP '.$this->qb_limit.' * FROM '.$table.$this->_compile_wh('qb_where').') DELETE FROM ci_delete'; + } + + return parent::_delete($table); + } + + // -------------------------------------------------------------------- + + /** + * LIMIT + * + * Generates a platform-specific LIMIT clause + * + * @param string $sql SQL Query + * @return string + */ + protected function _limit($sql) + { + // As of SQL Server 2012 (11.0.*) OFFSET is supported + if (version_compare($this->version(), '11', '>=')) + { + // SQL Server OFFSET-FETCH can be used only with the ORDER BY clause + empty($this->qb_orderby) && $sql .= ' ORDER BY 1'; + + return $sql.' OFFSET '.(int) $this->qb_offset.' ROWS FETCH NEXT '.$this->qb_limit.' ROWS ONLY'; + } + + $limit = $this->qb_offset + $this->qb_limit; + + // An ORDER BY clause is required for ROW_NUMBER() to work + if ($this->qb_offset && ! empty($this->qb_orderby)) + { + $orderby = $this->_compile_order_by(); + + // We have to strip the ORDER BY clause + $sql = trim(substr($sql, 0, strrpos($sql, $orderby))); + + // Get the fields to select from our subquery, so that we can avoid CI_rownum appearing in the actual results + if (count($this->qb_select) === 0) + { + $select = '*'; // Inevitable + } + else + { + // Use only field names and their aliases, everything else is out of our scope. + $select = array(); + $field_regexp = ($this->_quoted_identifier) + ? '("[^\"]+")' : '(\[[^\]]+\])'; + for ($i = 0, $c = count($this->qb_select); $i < $c; $i++) + { + $select[] = preg_match('/(?:\s|\.)'.$field_regexp.'$/i', $this->qb_select[$i], $m) + ? $m[1] : $this->qb_select[$i]; + } + $select = implode(', ', $select); + } + + return 'SELECT '.$select." FROM (\n\n" + .preg_replace('/^(SELECT( DISTINCT)?)/i', '\\1 ROW_NUMBER() OVER('.trim($orderby).') AS '.$this->escape_identifiers('CI_rownum').', ', $sql) + ."\n\n) ".$this->escape_identifiers('CI_subquery') + ."\nWHERE ".$this->escape_identifiers('CI_rownum').' BETWEEN '.($this->qb_offset + 1).' AND '.$limit; + } + + return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$limit.' ', $sql); + } + + // -------------------------------------------------------------------- + + /** + * Insert batch statement + * + * Generates a platform-specific insert string from the supplied data. + * + * @param string $table Table name + * @param array $keys INSERT keys + * @param array $values INSERT values + * @return string|bool + */ + protected function _insert_batch($table, $keys, $values) + { + // Multiple-value inserts are only supported as of SQL Server 2008 + if (version_compare($this->version(), '10', '>=')) + { + return parent::_insert_batch($table, $keys, $values); + } + + return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @return void + */ + protected function _close() + { + sqlsrv_close($this->conn_id); + } + +} diff --git a/codeigniter-3.0/system/database/drivers/sqlsrv/sqlsrv_forge.php b/ci-3.0/system/database/drivers/sqlsrv/sqlsrv_forge.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/sqlsrv/sqlsrv_forge.php rename to ci-3.0/system/database/drivers/sqlsrv/sqlsrv_forge.php diff --git a/codeigniter-3.0/system/database/drivers/sqlsrv/sqlsrv_result.php b/ci-3.0/system/database/drivers/sqlsrv/sqlsrv_result.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/sqlsrv/sqlsrv_result.php rename to ci-3.0/system/database/drivers/sqlsrv/sqlsrv_result.php diff --git a/codeigniter-3.0/system/database/drivers/sqlsrv/sqlsrv_utility.php b/ci-3.0/system/database/drivers/sqlsrv/sqlsrv_utility.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/drivers/sqlsrv/sqlsrv_utility.php rename to ci-3.0/system/database/drivers/sqlsrv/sqlsrv_utility.php diff --git a/codeigniter-3.0/system/database/index.html b/ci-3.0/system/database/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/database/index.html rename to ci-3.0/system/database/index.html diff --git a/codeigniter-3.0/system/fonts/index.html b/ci-3.0/system/fonts/index.html old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/fonts/index.html rename to ci-3.0/system/fonts/index.html diff --git a/codeigniter-3.0/system/fonts/texb.ttf b/ci-3.0/system/fonts/texb.ttf old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/fonts/texb.ttf rename to ci-3.0/system/fonts/texb.ttf diff --git a/ci-3.0/system/helpers/array_helper.php b/ci-3.0/system/helpers/array_helper.php new file mode 100755 index 000000000..2ce55b9c4 --- /dev/null +++ b/ci-3.0/system/helpers/array_helper.php @@ -0,0 +1,115 @@ + '', + 'img_path' => '', + 'img_url' => '', + 'img_width' => '150', + 'img_height' => '30', + 'font_path' => '', + 'expiration' => 7200, + 'word_length' => 8, + 'font_size' => 16, + 'img_id' => '', + 'pool' => '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', + 'colors' => array( + 'background' => array(255,255,255), + 'border' => array(153,102,102), + 'text' => array(204,153,153), + 'grid' => array(255,182,182) + ) + ); + + foreach ($defaults as $key => $val) + { + if ( ! is_array($data) && empty($$key)) + { + $$key = $val; + } + else + { + $$key = isset($data[$key]) ? $data[$key] : $val; + } + } + + if ($img_path === '' OR $img_url === '' + OR ! is_dir($img_path) OR ! is_really_writable($img_path) + OR ! extension_loaded('gd')) + { + return FALSE; + } + + // ----------------------------------- + // Remove old images + // ----------------------------------- + + $now = microtime(TRUE); + + $current_dir = @opendir($img_path); + while ($filename = @readdir($current_dir)) + { + if (substr($filename, -4) === '.jpg' && (str_replace('.jpg', '', $filename) + $expiration) < $now) + { + @unlink($img_path.$filename); + } + } + + @closedir($current_dir); + + // ----------------------------------- + // Do we have a "word" yet? + // ----------------------------------- + + if (empty($word)) + { + $word = ''; + $pool_length = strlen($pool); + $rand_max = $pool_length - 1; + + // PHP7 or a suitable polyfill + if (function_exists('random_int')) + { + try + { + for ($i = 0; $i < $word_length; $i++) + { + $word .= $pool[random_int(0, $rand_max)]; + } + } + catch (Exception $e) + { + // This means fallback to the next possible + // alternative to random_int() + $word = ''; + } + } + } + + if (empty($word)) + { + // Nobody will have a larger character pool than + // 256 characters, but let's handle it just in case ... + // + // No, I do not care that the fallback to mt_rand() can + // handle it; if you trigger this, you're very obviously + // trying to break it. -- Narf + if ($pool_length > 256) + { + return FALSE; + } + + // We'll try using the operating system's PRNG first, + // which we can access through CI_Security::get_random_bytes() + $security = get_instance()->security; + + // To avoid numerous get_random_bytes() calls, we'll + // just try fetching as much bytes as we need at once. + if (($bytes = $security->get_random_bytes($pool_length)) !== FALSE) + { + $byte_index = $word_index = 0; + while ($word_index < $word_length) + { + if (($rand_index = unpack('C', $bytes[$byte_index++])) > $rand_max) + { + // Was this the last byte we have? + // If so, try to fetch more. + if ($byte_index === $pool_length) + { + // No failures should be possible if + // the first get_random_bytes() call + // didn't return FALSE, but still ... + for ($i = 0; $i < 5; $i++) + { + if (($bytes = $security->get_random_bytes($pool_length)) === FALSE) + { + continue; + } + + $byte_index = 0; + break; + } + + if ($bytes === FALSE) + { + // Sadly, this means fallback to mt_rand() + $word = ''; + break; + } + } + + continue; + } + + $word .= $pool[$rand_index]; + $word_index++; + } + } + } + + if (empty($word)) + { + for ($i = 0; $i < $word_length; $i++) + { + $word .= $pool[mt_rand(0, $rand_max)]; + } + } + elseif ( ! is_string($word)) + { + $word = (string) $word; + } + + // ----------------------------------- + // Determine angle and position + // ----------------------------------- + $length = strlen($word); + $angle = ($length >= 6) ? mt_rand(-($length-6), ($length-6)) : 0; + $x_axis = mt_rand(6, (360/$length)-16); + $y_axis = ($angle >= 0) ? mt_rand($img_height, $img_width) : mt_rand(6, $img_height); + + // Create image + // PHP.net recommends imagecreatetruecolor(), but it isn't always available + $im = function_exists('imagecreatetruecolor') + ? imagecreatetruecolor($img_width, $img_height) + : imagecreate($img_width, $img_height); + + // ----------------------------------- + // Assign colors + // ---------------------------------- + + is_array($colors) OR $colors = $defaults['colors']; + + foreach (array_keys($defaults['colors']) as $key) + { + // Check for a possible missing value + is_array($colors[$key]) OR $colors[$key] = $defaults['colors'][$key]; + $colors[$key] = imagecolorallocate($im, $colors[$key][0], $colors[$key][1], $colors[$key][2]); + } + + // Create the rectangle + ImageFilledRectangle($im, 0, 0, $img_width, $img_height, $colors['background']); + + // ----------------------------------- + // Create the spiral pattern + // ----------------------------------- + $theta = 1; + $thetac = 7; + $radius = 16; + $circles = 20; + $points = 32; + + for ($i = 0, $cp = ($circles * $points) - 1; $i < $cp; $i++) + { + $theta += $thetac; + $rad = $radius * ($i / $points); + $x = ($rad * cos($theta)) + $x_axis; + $y = ($rad * sin($theta)) + $y_axis; + $theta += $thetac; + $rad1 = $radius * (($i + 1) / $points); + $x1 = ($rad1 * cos($theta)) + $x_axis; + $y1 = ($rad1 * sin($theta)) + $y_axis; + imageline($im, $x, $y, $x1, $y1, $colors['grid']); + $theta -= $thetac; + } + + // ----------------------------------- + // Write the text + // ----------------------------------- + + $use_font = ($font_path !== '' && file_exists($font_path) && function_exists('imagettftext')); + if ($use_font === FALSE) + { + ($font_size > 5) && $font_size = 5; + $x = mt_rand(0, $img_width / ($length / 3)); + $y = 0; + } + else + { + ($font_size > 30) && $font_size = 30; + $x = mt_rand(0, $img_width / ($length / 1.5)); + $y = $font_size + 2; + } + + for ($i = 0; $i < $length; $i++) + { + if ($use_font === FALSE) + { + $y = mt_rand(0 , $img_height / 2); + imagestring($im, $font_size, $x, $y, $word[$i], $colors['text']); + $x += ($font_size * 2); + } + else + { + $y = mt_rand($img_height / 2, $img_height - 3); + imagettftext($im, $font_size, $angle, $x, $y, $colors['text'], $font_path, $word[$i]); + $x += $font_size; + } + } + + // Create the border + imagerectangle($im, 0, 0, $img_width - 1, $img_height - 1, $colors['border']); + + // ----------------------------------- + // Generate the image + // ----------------------------------- + $img_url = rtrim($img_url, '/').'/'; + + if (function_exists('imagejpeg')) + { + $img_filename = $now.'.jpg'; + imagejpeg($im, $img_path.$img_filename); + } + elseif (function_exists('imagepng')) + { + $img_filename = $now.'.png'; + imagepng($im, $img_path.$img_filename); + } + else + { + return FALSE; + } + + $img = ' '; + ImageDestroy($im); + + return array('word' => $word, 'time' => $now, 'image' => $img, 'filename' => $img_filename); + } +} diff --git a/codeigniter-3.0/system/helpers/cookie_helper.php b/ci-3.0/system/helpers/cookie_helper.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/helpers/cookie_helper.php rename to ci-3.0/system/helpers/cookie_helper.php diff --git a/codeigniter-3.0/system/helpers/date_helper.php b/ci-3.0/system/helpers/date_helper.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/helpers/date_helper.php rename to ci-3.0/system/helpers/date_helper.php diff --git a/codeigniter-3.0/system/helpers/directory_helper.php b/ci-3.0/system/helpers/directory_helper.php old mode 100644 new mode 100755 similarity index 100% rename from codeigniter-3.0/system/helpers/directory_helper.php rename to ci-3.0/system/helpers/directory_helper.php diff --git a/ci-3.0/system/helpers/download_helper.php b/ci-3.0/system/helpers/download_helper.php new file mode 100755 index 000000000..73f6456c4 --- /dev/null +++ b/ci-3.0/system/helpers/download_helper.php @@ -0,0 +1,158 @@ + 0) + ? @rmdir($path) + : TRUE; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('get_filenames')) +{ + /** + * Get Filenames + * + * Reads the specified directory and builds an array containing the filenames. + * Any sub-folders contained within the specified path are read as well. + * + * @param string path to source + * @param bool whether to include the path as part of the filename + * @param bool internal variable to determine recursion status - do not use in calls + * @return array + */ + function get_filenames($source_dir, $include_path = FALSE, $_recursion = FALSE) + { + static $_filedata = array(); + + if ($fp = @opendir($source_dir)) + { + // reset the array and make sure $source_dir has a trailing slash on the initial call + if ($_recursion === FALSE) + { + $_filedata = array(); + $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; + } + + while (FALSE !== ($file = readdir($fp))) + { + if (is_dir($source_dir.$file) && $file[0] !== '.') + { + get_filenames($source_dir.$file.DIRECTORY_SEPARATOR, $include_path, TRUE); + } + elseif ($file[0] !== '.') + { + $_filedata[] = ($include_path === TRUE) ? $source_dir.$file : $file; + } + } + + closedir($fp); + return $_filedata; + } + + return FALSE; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('get_dir_file_info')) +{ + /** + * Get Directory File Information + * + * Reads the specified directory and builds an array containing the filenames, + * filesize, dates, and permissions + * + * Any sub-folders contained within the specified path are read as well. + * + * @param string path to source + * @param bool Look only at the top level directory specified? + * @param bool internal variable to determine recursion status - do not use in calls + * @return array + */ + function get_dir_file_info($source_dir, $top_level_only = TRUE, $_recursion = FALSE) + { + static $_filedata = array(); + $relative_path = $source_dir; + + if ($fp = @opendir($source_dir)) + { + // reset the array and make sure $source_dir has a trailing slash on the initial call + if ($_recursion === FALSE) + { + $_filedata = array(); + $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; + } + + // Used to be foreach (scandir($source_dir, 1) as $file), but scandir() is simply not as fast + while (FALSE !== ($file = readdir($fp))) + { + if (is_dir($source_dir.$file) && $file[0] !== '.' && $top_level_only === FALSE) + { + get_dir_file_info($source_dir.$file.DIRECTORY_SEPARATOR, $top_level_only, TRUE); + } + elseif ($file[0] !== '.') + { + $_filedata[$file] = get_file_info($source_dir.$file); + $_filedata[$file]['relative_path'] = $relative_path; + } + } + + closedir($fp); + return $_filedata; + } + + return FALSE; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('get_file_info')) +{ + /** + * Get File Info + * + * Given a file and path, returns the name, path, size, date modified + * Second parameter allows you to explicitly declare what information you want returned + * Options are: name, server_path, size, date, readable, writable, executable, fileperms + * Returns FALSE if the file cannot be found. + * + * @param string path to file + * @param mixed array or comma separated string of information returned + * @return array + */ + function get_file_info($file, $returned_values = array('name', 'server_path', 'size', 'date')) + { + if ( ! file_exists($file)) + { + return FALSE; + } + + if (is_string($returned_values)) + { + $returned_values = explode(',', $returned_values); + } + + foreach ($returned_values as $key) + { + switch ($key) + { + case 'name': + $fileinfo['name'] = basename($file); + break; + case 'server_path': + $fileinfo['server_path'] = $file; + break; + case 'size': + $fileinfo['size'] = filesize($file); + break; + case 'date': + $fileinfo['date'] = filemtime($file); + break; + case 'readable': + $fileinfo['readable'] = is_readable($file); + break; + case 'writable': + $fileinfo['writable'] = is_really_writable($file); + break; + case 'executable': + $fileinfo['executable'] = is_executable($file); + break; + case 'fileperms': + $fileinfo['fileperms'] = fileperms($file); + break; + } + } + + return $fileinfo; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('get_mime_by_extension')) +{ + /** + * Get Mime by Extension + * + * Translates a file extension into a mime type based on config/mimes.php. + * Returns FALSE if it can't determine the type, or open the mime config file + * + * Note: this is NOT an accurate way of determining file mime types, and is here strictly as a convenience + * It should NOT be trusted, and should certainly NOT be used for security + * + * @param string $filename File name + * @return string + */ + function get_mime_by_extension($filename) + { + static $mimes; + + if ( ! is_array($mimes)) + { + $mimes = get_mimes(); + + if (empty($mimes)) + { + return FALSE; + } + } + + $extension = strtolower(substr(strrchr($filename, '.'), 1)); + + if (isset($mimes[$extension])) + { + return is_array($mimes[$extension]) + ? current($mimes[$extension]) // Multiple mime types, just give the first one + : $mimes[$extension]; + } + + return FALSE; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('symbolic_permissions')) +{ + /** + * Symbolic Permissions + * + * Takes a numeric value representing a file's permissions and returns + * standard symbolic notation representing that value + * + * @param int $perms Permissions + * @return string + */ + function symbolic_permissions($perms) + { + if (($perms & 0xC000) === 0xC000) + { + $symbolic = 's'; // Socket + } + elseif (($perms & 0xA000) === 0xA000) + { + $symbolic = 'l'; // Symbolic Link + } + elseif (($perms & 0x8000) === 0x8000) + { + $symbolic = '-'; // Regular + } + elseif (($perms & 0x6000) === 0x6000) + { + $symbolic = 'b'; // Block special + } + elseif (($perms & 0x4000) === 0x4000) + { + $symbolic = 'd'; // Directory + } + elseif (($perms & 0x2000) === 0x2000) + { + $symbolic = 'c'; // Character special + } + elseif (($perms & 0x1000) === 0x1000) + { + $symbolic = 'p'; // FIFO pipe + } + else + { + $symbolic = 'u'; // Unknown + } + + // Owner + $symbolic .= (($perms & 0x0100) ? 'r' : '-') + .(($perms & 0x0080) ? 'w' : '-') + .(($perms & 0x0040) ? (($perms & 0x0800) ? 's' : 'x' ) : (($perms & 0x0800) ? 'S' : '-')); + + // Group + $symbolic .= (($perms & 0x0020) ? 'r' : '-') + .(($perms & 0x0010) ? 'w' : '-') + .(($perms & 0x0008) ? (($perms & 0x0400) ? 's' : 'x' ) : (($perms & 0x0400) ? 'S' : '-')); + + // World + $symbolic .= (($perms & 0x0004) ? 'r' : '-') + .(($perms & 0x0002) ? 'w' : '-') + .(($perms & 0x0001) ? (($perms & 0x0200) ? 't' : 'x' ) : (($perms & 0x0200) ? 'T' : '-')); + + return $symbolic; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('octal_permissions')) +{ + /** + * Octal Permissions + * + * Takes a numeric value representing a file's permissions and returns + * a three character string representing the file's octal permissions + * + * @param int $perms Permissions + * @return string + */ + function octal_permissions($perms) + { + return substr(sprintf('%o', $perms), -3); + } +} diff --git a/ci-3.0/system/helpers/form_helper.php b/ci-3.0/system/helpers/form_helper.php new file mode 100755 index 000000000..fd807769a --- /dev/null +++ b/ci-3.0/system/helpers/form_helper.php @@ -0,0 +1,1007 @@ +config->site_url($CI->uri->uri_string()); + } + // If an action is not a full URL then turn it into one + elseif (strpos($action, '://') === FALSE) + { + $action = $CI->config->site_url($action); + } + + $attributes = _attributes_to_string($attributes); + + if (stripos($attributes, 'method=') === FALSE) + { + $attributes .= ' method="post"'; + } + + if (stripos($attributes, 'accept-charset=') === FALSE) + { + $attributes .= ' accept-charset="'.strtolower(config_item('charset')).'"'; + } + + $form = '
\n"; + + // Add CSRF field if enabled, but leave it out for GET requests and requests to external websites + if ($CI->config->item('csrf_protection') === TRUE && strpos($action, $CI->config->base_url()) !== FALSE && ! stripos($form, 'method="get"')) + { + $hidden[$CI->security->get_csrf_token_name()] = $CI->security->get_csrf_hash(); + } + + if (is_array($hidden)) + { + foreach ($hidden as $name => $value) + { + $form .= ''."\n"; + } + } + + return $form; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_open_multipart')) +{ + /** + * Form Declaration - Multipart type + * + * Creates the opening portion of the form, but with "multipart/form-data". + * + * @param string the URI segments of the form destination + * @param array a key/value pair of attributes + * @param array a key/value pair hidden data + * @return string + */ + function form_open_multipart($action = '', $attributes = array(), $hidden = array()) + { + if (is_string($attributes)) + { + $attributes .= ' enctype="multipart/form-data"'; + } + else + { + $attributes['enctype'] = 'multipart/form-data'; + } + + return form_open($action, $attributes, $hidden); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_hidden')) +{ + /** + * Hidden Input Field + * + * Generates hidden fields. You can pass a simple key/value string or + * an associative array with multiple values. + * + * @param mixed $name Field name + * @param string $value Field value + * @param bool $recursing + * @return string + */ + function form_hidden($name, $value = '', $recursing = FALSE) + { + static $form; + + if ($recursing === FALSE) + { + $form = "\n"; + } + + if (is_array($name)) + { + foreach ($name as $key => $val) + { + form_hidden($key, $val, TRUE); + } + + return $form; + } + + if ( ! is_array($value)) + { + $form .= '\n"; + } + else + { + foreach ($value as $k => $v) + { + $k = is_int($k) ? '' : $k; + form_hidden($name.'['.$k.']', $v, TRUE); + } + } + + return $form; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_input')) +{ + /** + * Text Input Field + * + * @param mixed + * @param string + * @param mixed + * @return string + */ + function form_input($data = '', $value = '', $extra = '') + { + $defaults = array( + 'type' => 'text', + 'name' => is_array($data) ? '' : $data, + 'value' => $value + ); + + return '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_password')) +{ + /** + * Password Field + * + * Identical to the input function but adds the "password" type + * + * @param mixed + * @param string + * @param mixed + * @return string + */ + function form_password($data = '', $value = '', $extra = '') + { + is_array($data) OR $data = array('name' => $data); + $data['type'] = 'password'; + return form_input($data, $value, $extra); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_upload')) +{ + /** + * Upload Field + * + * Identical to the input function but adds the "file" type + * + * @param mixed + * @param string + * @param mixed + * @return string + */ + function form_upload($data = '', $value = '', $extra = '') + { + $defaults = array('type' => 'file', 'name' => ''); + is_array($data) OR $data = array('name' => $data); + $data['type'] = 'file'; + + return '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_textarea')) +{ + /** + * Textarea field + * + * @param mixed $data + * @param string $value + * @param mixed $extra + * @return string + */ + function form_textarea($data = '', $value = '', $extra = '') + { + $defaults = array( + 'name' => is_array($data) ? '' : $data, + 'cols' => '40', + 'rows' => '10' + ); + + if ( ! is_array($data) OR ! isset($data['value'])) + { + $val = $value; + } + else + { + $val = $data['value']; + unset($data['value']); // textareas don't use the value attribute + } + + return '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_multiselect')) +{ + /** + * Multi-select menu + * + * @param string + * @param array + * @param mixed + * @param mixed + * @return string + */ + function form_multiselect($name = '', $options = array(), $selected = array(), $extra = '') + { + $extra = _attributes_to_string($extra); + if (stripos($extra, 'multiple') === FALSE) + { + $extra .= ' multiple="multiple"'; + } + + return form_dropdown($name, $options, $selected, $extra); + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('form_dropdown')) +{ + /** + * Drop-down Menu + * + * @param mixed $data + * @param mixed $options + * @param mixed $selected + * @param mixed $extra + * @return string + */ + function form_dropdown($data = '', $options = array(), $selected = array(), $extra = '') + { + $defaults = array(); + + if (is_array($data)) + { + if (isset($data['selected'])) + { + $selected = $data['selected']; + unset($data['selected']); // select tags don't have a selected attribute + } + + if (isset($data['options'])) + { + $options = $data['options']; + unset($data['options']); // select tags don't use an options attribute + } + } + else + { + $defaults = array('name' => $data); + } + + is_array($selected) OR $selected = array($selected); + is_array($options) OR $options = array($options); + + // If no selected state was submitted we will attempt to set it automatically + if (empty($selected)) + { + if (is_array($data)) + { + if (isset($data['name'], $_POST[$data['name']])) + { + $selected = array($_POST[$data['name']]); + } + } + elseif (isset($_POST[$data])) + { + $selected = array($_POST[$data]); + } + } + + $extra = _attributes_to_string($extra); + + $multiple = (count($selected) > 1 && stripos($extra, 'multiple') === FALSE) ? ' multiple="multiple"' : ''; + + $form = '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_checkbox')) +{ + /** + * Checkbox Field + * + * @param mixed + * @param string + * @param bool + * @param mixed + * @return string + */ + function form_checkbox($data = '', $value = '', $checked = FALSE, $extra = '') + { + $defaults = array('type' => 'checkbox', 'name' => ( ! is_array($data) ? $data : ''), 'value' => $value); + + if (is_array($data) && array_key_exists('checked', $data)) + { + $checked = $data['checked']; + + if ($checked == FALSE) + { + unset($data['checked']); + } + else + { + $data['checked'] = 'checked'; + } + } + + if ($checked == TRUE) + { + $defaults['checked'] = 'checked'; + } + else + { + unset($defaults['checked']); + } + + return '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_radio')) +{ + /** + * Radio Button + * + * @param mixed + * @param string + * @param bool + * @param mixed + * @return string + */ + function form_radio($data = '', $value = '', $checked = FALSE, $extra = '') + { + is_array($data) OR $data = array('name' => $data); + $data['type'] = 'radio'; + + return form_checkbox($data, $value, $checked, $extra); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_submit')) +{ + /** + * Submit Button + * + * @param mixed + * @param string + * @param mixed + * @return string + */ + function form_submit($data = '', $value = '', $extra = '') + { + $defaults = array( + 'type' => 'submit', + 'name' => is_array($data) ? '' : $data, + 'value' => $value + ); + + return '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_reset')) +{ + /** + * Reset Button + * + * @param mixed + * @param string + * @param mixed + * @return string + */ + function form_reset($data = '', $value = '', $extra = '') + { + $defaults = array( + 'type' => 'reset', + 'name' => is_array($data) ? '' : $data, + 'value' => $value + ); + + return '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_button')) +{ + /** + * Form Button + * + * @param mixed + * @param string + * @param mixed + * @return string + */ + function form_button($data = '', $content = '', $extra = '') + { + $defaults = array( + 'name' => is_array($data) ? '' : $data, + 'type' => 'button' + ); + + if (is_array($data) && isset($data['content'])) + { + $content = $data['content']; + unset($data['content']); // content is not an attribute + } + + return '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_label')) +{ + /** + * Form Label Tag + * + * @param string The text to appear onscreen + * @param string The id the label applies to + * @param string Additional attributes + * @return string + */ + function form_label($label_text = '', $id = '', $attributes = array()) + { + + $label = ' 0) + { + foreach ($attributes as $key => $val) + { + $label .= ' '.$key.'="'.$val.'"'; + } + } + + return $label.'>'.$label_text.''; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_fieldset')) +{ + /** + * Fieldset Tag + * + * Used to produce
text. To close fieldset + * use form_fieldset_close() + * + * @param string The legend text + * @param array Additional attributes + * @return string + */ + function form_fieldset($legend_text = '', $attributes = array()) + { + $fieldset = '\n"; + if ($legend_text !== '') + { + return $fieldset.''.$legend_text."\n"; + } + + return $fieldset; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_fieldset_close')) +{ + /** + * Fieldset Close Tag + * + * @param string + * @return string + */ + function form_fieldset_close($extra = '') + { + return '
'.$extra; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_close')) +{ + /** + * Form Close Tag + * + * @param string + * @return string + */ + function form_close($extra = '') + { + return ''.$extra; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_prep')) +{ + /** + * Form Prep + * + * Formats text so that it can be safely placed in a form field in the event it has HTML tags. + * + * @deprecated 3.0.0 An alias for html_escape() + * @param string|string[] $str Value to escape + * @return string|string[] Escaped values + */ + function form_prep($str) + { + return html_escape($str, TRUE); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('set_value')) +{ + /** + * Form Value + * + * Grabs a value from the POST array for the specified field so you can + * re-populate an input field or textarea. If Form Validation + * is active it retrieves the info from the validation class + * + * @param string $field Field name + * @param string $default Default value + * @param bool $html_escape Whether to escape HTML special characters or not + * @return string + */ + function set_value($field, $default = '', $html_escape = TRUE) + { + $CI =& get_instance(); + + $value = (isset($CI->form_validation) && is_object($CI->form_validation) && $CI->form_validation->has_rule($field)) + ? $CI->form_validation->set_value($field, $default) + : $CI->input->post($field, FALSE); + + isset($value) OR $value = $default; + return ($html_escape) ? html_escape($value) : $value; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('set_select')) +{ + /** + * Set Select + * + * Let's you set the selected value of a \n"; + + foreach (timezones() as $key => $val) + { + $selected = ($default === $key) ? ' selected="selected"' : ''; + $menu .= '\n"; + } + + return $menu.''; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('timezones')) +{ + /** + * Timezones + * + * Returns an array of timezones. This is a helper function + * for various other ones in this library + * + * @param string timezone + * @return string + */ + function timezones($tz = '') + { + // Note: Don't change the order of these even though + // some items appear to be in the wrong order + + $zones = array( + 'UM12' => -12, + 'UM11' => -11, + 'UM10' => -10, + 'UM95' => -9.5, + 'UM9' => -9, + 'UM8' => -8, + 'UM7' => -7, + 'UM6' => -6, + 'UM5' => -5, + 'UM45' => -4.5, + 'UM4' => -4, + 'UM35' => -3.5, + 'UM3' => -3, + 'UM2' => -2, + 'UM1' => -1, + 'UTC' => 0, + 'UP1' => +1, + 'UP2' => +2, + 'UP3' => +3, + 'UP35' => +3.5, + 'UP4' => +4, + 'UP45' => +4.5, + 'UP5' => +5, + 'UP55' => +5.5, + 'UP575' => +5.75, + 'UP6' => +6, + 'UP65' => +6.5, + 'UP7' => +7, + 'UP8' => +8, + 'UP875' => +8.75, + 'UP9' => +9, + 'UP95' => +9.5, + 'UP10' => +10, + 'UP105' => +10.5, + 'UP11' => +11, + 'UP115' => +11.5, + 'UP12' => +12, + 'UP1275' => +12.75, + 'UP13' => +13, + 'UP14' => +14 + ); + + if ($tz === '') + { + return $zones; + } + + return isset($zones[$tz]) ? $zones[$tz] : 0; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('date_range')) +{ + /** + * Date range + * + * Returns a list of dates within a specified period. + * + * @param int unix_start UNIX timestamp of period start date + * @param int unix_end|days UNIX timestamp of period end date + * or interval in days. + * @param mixed is_unix Specifies whether the second parameter + * is a UNIX timestamp or a day interval + * - TRUE or 'unix' for a timestamp + * - FALSE or 'days' for an interval + * @param string date_format Output date format, same as in date() + * @return array + */ + function date_range($unix_start = '', $mixed = '', $is_unix = TRUE, $format = 'Y-m-d') + { + if ($unix_start == '' OR $mixed == '' OR $format == '') + { + return FALSE; + } + + $is_unix = ! ( ! $is_unix OR $is_unix === 'days'); + + // Validate input and try strtotime() on invalid timestamps/intervals, just in case + if ( ( ! ctype_digit((string) $unix_start) && ($unix_start = @strtotime($unix_start)) === FALSE) + OR ( ! ctype_digit((string) $mixed) && ($is_unix === FALSE OR ($mixed = @strtotime($mixed)) === FALSE)) + OR ($is_unix === TRUE && $mixed < $unix_start)) + { + return FALSE; + } + + if ($is_unix && ($unix_start == $mixed OR date($format, $unix_start) === date($format, $mixed))) + { + return array(date($format, $unix_start)); + } + + $range = array(); + + $from = new DateTime(); + $from->setTimestamp($unix_start); + + if ($is_unix) + { + $arg = new DateTime(); + $arg->setTimestamp($mixed); + } + else + { + $arg = (int) $mixed; + } + + $period = new DatePeriod($from, new DateInterval('P1D'), $arg); + foreach ($period as $date) + { + $range[] = $date->format($format); + } + + /* If a period end date was passed to the DatePeriod constructor, it might not + * be in our results. Not sure if this is a bug or it's just possible because + * the end date might actually be less than 24 hours away from the previously + * generated DateTime object, but either way - we have to append it manually. + */ + if ( ! is_int($arg) && $range[count($range) - 1] !== $arg->format($format)) + { + $range[] = $arg->format($format); + } + + return $range; + } +} diff --git a/ci-3.1/system/helpers/directory_helper.php b/ci-3.1/system/helpers/directory_helper.php new file mode 100644 index 000000000..2785241e6 --- /dev/null +++ b/ci-3.1/system/helpers/directory_helper.php @@ -0,0 +1,101 @@ + 0) && is_dir($source_dir.$file)) + { + $filedata[$file] = directory_map($source_dir.$file, $new_depth, $hidden); + } + else + { + $filedata[] = $file; + } + } + + closedir($fp); + return $filedata; + } + + return FALSE; + } +} diff --git a/ci-3.1/system/helpers/download_helper.php b/ci-3.1/system/helpers/download_helper.php new file mode 100644 index 000000000..b2a1458de --- /dev/null +++ b/ci-3.1/system/helpers/download_helper.php @@ -0,0 +1,158 @@ + 0) + ? @rmdir($path) + : TRUE; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('get_filenames')) +{ + /** + * Get Filenames + * + * Reads the specified directory and builds an array containing the filenames. + * Any sub-folders contained within the specified path are read as well. + * + * @param string path to source + * @param bool whether to include the path as part of the filename + * @param bool internal variable to determine recursion status - do not use in calls + * @return array + */ + function get_filenames($source_dir, $include_path = FALSE, $_recursion = FALSE) + { + static $_filedata = array(); + + if ($fp = @opendir($source_dir)) + { + // reset the array and make sure $source_dir has a trailing slash on the initial call + if ($_recursion === FALSE) + { + $_filedata = array(); + $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; + } + + while (FALSE !== ($file = readdir($fp))) + { + if (is_dir($source_dir.$file) && $file[0] !== '.') + { + get_filenames($source_dir.$file.DIRECTORY_SEPARATOR, $include_path, TRUE); + } + elseif ($file[0] !== '.') + { + $_filedata[] = ($include_path === TRUE) ? $source_dir.$file : $file; + } + } + + closedir($fp); + return $_filedata; + } + + return FALSE; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('get_dir_file_info')) +{ + /** + * Get Directory File Information + * + * Reads the specified directory and builds an array containing the filenames, + * filesize, dates, and permissions + * + * Any sub-folders contained within the specified path are read as well. + * + * @param string path to source + * @param bool Look only at the top level directory specified? + * @param bool internal variable to determine recursion status - do not use in calls + * @return array + */ + function get_dir_file_info($source_dir, $top_level_only = TRUE, $_recursion = FALSE) + { + static $_filedata = array(); + $relative_path = $source_dir; + + if ($fp = @opendir($source_dir)) + { + // reset the array and make sure $source_dir has a trailing slash on the initial call + if ($_recursion === FALSE) + { + $_filedata = array(); + $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; + } + + // Used to be foreach (scandir($source_dir, 1) as $file), but scandir() is simply not as fast + while (FALSE !== ($file = readdir($fp))) + { + if (is_dir($source_dir.$file) && $file[0] !== '.' && $top_level_only === FALSE) + { + get_dir_file_info($source_dir.$file.DIRECTORY_SEPARATOR, $top_level_only, TRUE); + } + elseif ($file[0] !== '.') + { + $_filedata[$file] = get_file_info($source_dir.$file); + $_filedata[$file]['relative_path'] = $relative_path; + } + } + + closedir($fp); + return $_filedata; + } + + return FALSE; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('get_file_info')) +{ + /** + * Get File Info + * + * Given a file and path, returns the name, path, size, date modified + * Second parameter allows you to explicitly declare what information you want returned + * Options are: name, server_path, size, date, readable, writable, executable, fileperms + * Returns FALSE if the file cannot be found. + * + * @param string path to file + * @param mixed array or comma separated string of information returned + * @return array + */ + function get_file_info($file, $returned_values = array('name', 'server_path', 'size', 'date')) + { + if ( ! file_exists($file)) + { + return FALSE; + } + + if (is_string($returned_values)) + { + $returned_values = explode(',', $returned_values); + } + + foreach ($returned_values as $key) + { + switch ($key) + { + case 'name': + $fileinfo['name'] = basename($file); + break; + case 'server_path': + $fileinfo['server_path'] = $file; + break; + case 'size': + $fileinfo['size'] = filesize($file); + break; + case 'date': + $fileinfo['date'] = filemtime($file); + break; + case 'readable': + $fileinfo['readable'] = is_readable($file); + break; + case 'writable': + $fileinfo['writable'] = is_really_writable($file); + break; + case 'executable': + $fileinfo['executable'] = is_executable($file); + break; + case 'fileperms': + $fileinfo['fileperms'] = fileperms($file); + break; + } + } + + return $fileinfo; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('get_mime_by_extension')) +{ + /** + * Get Mime by Extension + * + * Translates a file extension into a mime type based on config/mimes.php. + * Returns FALSE if it can't determine the type, or open the mime config file + * + * Note: this is NOT an accurate way of determining file mime types, and is here strictly as a convenience + * It should NOT be trusted, and should certainly NOT be used for security + * + * @param string $filename File name + * @return string + */ + function get_mime_by_extension($filename) + { + static $mimes; + + if ( ! is_array($mimes)) + { + $mimes = get_mimes(); + + if (empty($mimes)) + { + return FALSE; + } + } + + $extension = strtolower(substr(strrchr($filename, '.'), 1)); + + if (isset($mimes[$extension])) + { + return is_array($mimes[$extension]) + ? current($mimes[$extension]) // Multiple mime types, just give the first one + : $mimes[$extension]; + } + + return FALSE; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('symbolic_permissions')) +{ + /** + * Symbolic Permissions + * + * Takes a numeric value representing a file's permissions and returns + * standard symbolic notation representing that value + * + * @param int $perms Permissions + * @return string + */ + function symbolic_permissions($perms) + { + if (($perms & 0xC000) === 0xC000) + { + $symbolic = 's'; // Socket + } + elseif (($perms & 0xA000) === 0xA000) + { + $symbolic = 'l'; // Symbolic Link + } + elseif (($perms & 0x8000) === 0x8000) + { + $symbolic = '-'; // Regular + } + elseif (($perms & 0x6000) === 0x6000) + { + $symbolic = 'b'; // Block special + } + elseif (($perms & 0x4000) === 0x4000) + { + $symbolic = 'd'; // Directory + } + elseif (($perms & 0x2000) === 0x2000) + { + $symbolic = 'c'; // Character special + } + elseif (($perms & 0x1000) === 0x1000) + { + $symbolic = 'p'; // FIFO pipe + } + else + { + $symbolic = 'u'; // Unknown + } + + // Owner + $symbolic .= (($perms & 0x0100) ? 'r' : '-') + .(($perms & 0x0080) ? 'w' : '-') + .(($perms & 0x0040) ? (($perms & 0x0800) ? 's' : 'x' ) : (($perms & 0x0800) ? 'S' : '-')); + + // Group + $symbolic .= (($perms & 0x0020) ? 'r' : '-') + .(($perms & 0x0010) ? 'w' : '-') + .(($perms & 0x0008) ? (($perms & 0x0400) ? 's' : 'x' ) : (($perms & 0x0400) ? 'S' : '-')); + + // World + $symbolic .= (($perms & 0x0004) ? 'r' : '-') + .(($perms & 0x0002) ? 'w' : '-') + .(($perms & 0x0001) ? (($perms & 0x0200) ? 't' : 'x' ) : (($perms & 0x0200) ? 'T' : '-')); + + return $symbolic; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('octal_permissions')) +{ + /** + * Octal Permissions + * + * Takes a numeric value representing a file's permissions and returns + * a three character string representing the file's octal permissions + * + * @param int $perms Permissions + * @return string + */ + function octal_permissions($perms) + { + return substr(sprintf('%o', $perms), -3); + } +} diff --git a/ci-3.1/system/helpers/form_helper.php b/ci-3.1/system/helpers/form_helper.php new file mode 100644 index 000000000..a49eea803 --- /dev/null +++ b/ci-3.1/system/helpers/form_helper.php @@ -0,0 +1,1061 @@ +config->site_url($CI->uri->uri_string()); + } + // If an action is not a full URL then turn it into one + elseif (strpos($action, '://') === FALSE) + { + $action = $CI->config->site_url($action); + } + + $attributes = _attributes_to_string($attributes); + + if (stripos($attributes, 'method=') === FALSE) + { + $attributes .= ' method="post"'; + } + + if (stripos($attributes, 'accept-charset=') === FALSE) + { + $attributes .= ' accept-charset="'.strtolower(config_item('charset')).'"'; + } + + $form = '
\n"; + + if (is_array($hidden)) + { + foreach ($hidden as $name => $value) + { + $form .= ''."\n"; + } + } + + // Add CSRF field if enabled, but leave it out for GET requests and requests to external websites + if ($CI->config->item('csrf_protection') === TRUE && strpos($action, $CI->config->base_url()) !== FALSE && ! stripos($form, 'method="get"')) + { + // Prepend/append random-length "white noise" around the CSRF + // token input, as a form of protection against BREACH attacks + if (FALSE !== ($noise = $CI->security->get_random_bytes(1))) + { + list(, $noise) = unpack('c', $noise); + } + else + { + $noise = mt_rand(-128, 127); + } + + // Prepend if $noise has a negative value, append if positive, do nothing for zero + $prepend = $append = ''; + if ($noise < 0) + { + $prepend = str_repeat(" ", abs($noise)); + } + elseif ($noise > 0) + { + $append = str_repeat(" ", $noise); + } + + $form .= sprintf( + '%s%s%s', + $prepend, + $CI->security->get_csrf_token_name(), + $CI->security->get_csrf_hash(), + $append, + "\n" + ); + } + + return $form; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_open_multipart')) +{ + /** + * Form Declaration - Multipart type + * + * Creates the opening portion of the form, but with "multipart/form-data". + * + * @param string the URI segments of the form destination + * @param array a key/value pair of attributes + * @param array a key/value pair hidden data + * @return string + */ + function form_open_multipart($action = '', $attributes = array(), $hidden = array()) + { + if (is_string($attributes)) + { + $attributes .= ' enctype="multipart/form-data"'; + } + else + { + $attributes['enctype'] = 'multipart/form-data'; + } + + return form_open($action, $attributes, $hidden); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_hidden')) +{ + /** + * Hidden Input Field + * + * Generates hidden fields. You can pass a simple key/value string or + * an associative array with multiple values. + * + * @param mixed $name Field name + * @param string $value Field value + * @param bool $recursing + * @return string + */ + function form_hidden($name, $value = '', $recursing = FALSE) + { + static $form; + + if ($recursing === FALSE) + { + $form = "\n"; + } + + if (is_array($name)) + { + foreach ($name as $key => $val) + { + form_hidden($key, $val, TRUE); + } + + return $form; + } + + if ( ! is_array($value)) + { + $form .= '\n"; + } + else + { + foreach ($value as $k => $v) + { + $k = is_int($k) ? '' : $k; + form_hidden($name.'['.$k.']', $v, TRUE); + } + } + + return $form; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_input')) +{ + /** + * Text Input Field + * + * @param mixed + * @param string + * @param mixed + * @return string + */ + function form_input($data = '', $value = '', $extra = '') + { + $defaults = array( + 'type' => 'text', + 'name' => is_array($data) ? '' : $data, + 'value' => $value + ); + + return '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_password')) +{ + /** + * Password Field + * + * Identical to the input function but adds the "password" type + * + * @param mixed + * @param string + * @param mixed + * @return string + */ + function form_password($data = '', $value = '', $extra = '') + { + is_array($data) OR $data = array('name' => $data); + $data['type'] = 'password'; + return form_input($data, $value, $extra); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_upload')) +{ + /** + * Upload Field + * + * Identical to the input function but adds the "file" type + * + * @param mixed + * @param string + * @param mixed + * @return string + */ + function form_upload($data = '', $value = '', $extra = '') + { + $defaults = array('type' => 'file', 'name' => ''); + is_array($data) OR $data = array('name' => $data); + $data['type'] = 'file'; + + return '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_textarea')) +{ + /** + * Textarea field + * + * @param mixed $data + * @param string $value + * @param mixed $extra + * @return string + */ + function form_textarea($data = '', $value = '', $extra = '') + { + $defaults = array( + 'name' => is_array($data) ? '' : $data, + 'cols' => '40', + 'rows' => '10' + ); + + if ( ! is_array($data) OR ! isset($data['value'])) + { + $val = $value; + } + else + { + $val = $data['value']; + unset($data['value']); // textareas don't use the value attribute + } + + return '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_multiselect')) +{ + /** + * Multi-select menu + * + * @param string + * @param array + * @param mixed + * @param mixed + * @return string + */ + function form_multiselect($name = '', $options = array(), $selected = array(), $extra = '') + { + $extra = _attributes_to_string($extra); + if (stripos($extra, 'multiple') === FALSE) + { + $extra .= ' multiple="multiple"'; + } + + return form_dropdown($name, $options, $selected, $extra); + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('form_dropdown')) +{ + /** + * Drop-down Menu + * + * @param mixed $data + * @param mixed $options + * @param mixed $selected + * @param mixed $extra + * @return string + */ + function form_dropdown($data = '', $options = array(), $selected = array(), $extra = '') + { + $defaults = array(); + + if (is_array($data)) + { + if (isset($data['selected'])) + { + $selected = $data['selected']; + unset($data['selected']); // select tags don't have a selected attribute + } + + if (isset($data['options'])) + { + $options = $data['options']; + unset($data['options']); // select tags don't use an options attribute + } + } + else + { + $defaults = array('name' => $data); + } + + is_array($selected) OR $selected = array($selected); + is_array($options) OR $options = array($options); + + // If no selected state was submitted we will attempt to set it automatically + if (empty($selected)) + { + if (is_array($data)) + { + if (isset($data['name'], $_POST[$data['name']])) + { + $selected = array($_POST[$data['name']]); + } + } + elseif (isset($_POST[$data])) + { + $selected = array($_POST[$data]); + } + } + + $extra = _attributes_to_string($extra); + + $multiple = (count($selected) > 1 && stripos($extra, 'multiple') === FALSE) ? ' multiple="multiple"' : ''; + + $form = '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_checkbox')) +{ + /** + * Checkbox Field + * + * @param mixed + * @param string + * @param bool + * @param mixed + * @return string + */ + function form_checkbox($data = '', $value = '', $checked = FALSE, $extra = '') + { + $defaults = array('type' => 'checkbox', 'name' => ( ! is_array($data) ? $data : ''), 'value' => $value); + + if (is_array($data) && array_key_exists('checked', $data)) + { + $checked = $data['checked']; + + if ($checked == FALSE) + { + unset($data['checked']); + } + else + { + $data['checked'] = 'checked'; + } + } + + if ($checked == TRUE) + { + $defaults['checked'] = 'checked'; + } + else + { + unset($defaults['checked']); + } + + return '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_radio')) +{ + /** + * Radio Button + * + * @param mixed + * @param string + * @param bool + * @param mixed + * @return string + */ + function form_radio($data = '', $value = '', $checked = FALSE, $extra = '') + { + is_array($data) OR $data = array('name' => $data); + $data['type'] = 'radio'; + + return form_checkbox($data, $value, $checked, $extra); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_submit')) +{ + /** + * Submit Button + * + * @param mixed + * @param string + * @param mixed + * @return string + */ + function form_submit($data = '', $value = '', $extra = '') + { + $defaults = array( + 'type' => 'submit', + 'name' => is_array($data) ? '' : $data, + 'value' => $value + ); + + return '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_reset')) +{ + /** + * Reset Button + * + * @param mixed + * @param string + * @param mixed + * @return string + */ + function form_reset($data = '', $value = '', $extra = '') + { + $defaults = array( + 'type' => 'reset', + 'name' => is_array($data) ? '' : $data, + 'value' => $value + ); + + return '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_button')) +{ + /** + * Form Button + * + * @param mixed + * @param string + * @param mixed + * @return string + */ + function form_button($data = '', $content = '', $extra = '') + { + $defaults = array( + 'name' => is_array($data) ? '' : $data, + 'type' => 'button' + ); + + if (is_array($data) && isset($data['content'])) + { + $content = $data['content']; + unset($data['content']); // content is not an attribute + } + + return '\n"; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_label')) +{ + /** + * Form Label Tag + * + * @param string The text to appear onscreen + * @param string The id the label applies to + * @param array Additional attributes + * @return string + */ + function form_label($label_text = '', $id = '', $attributes = array()) + { + + $label = ' 0) + { + foreach ($attributes as $key => $val) + { + $label .= ' '.$key.'="'.$val.'"'; + } + } + + return $label.'>'.$label_text.''; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_fieldset')) +{ + /** + * Fieldset Tag + * + * Used to produce
text. To close fieldset + * use form_fieldset_close() + * + * @param string The legend text + * @param array Additional attributes + * @return string + */ + function form_fieldset($legend_text = '', $attributes = array()) + { + $fieldset = '\n"; + if ($legend_text !== '') + { + return $fieldset.''.$legend_text."\n"; + } + + return $fieldset; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_fieldset_close')) +{ + /** + * Fieldset Close Tag + * + * @param string + * @return string + */ + function form_fieldset_close($extra = '') + { + return '
'.$extra; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_close')) +{ + /** + * Form Close Tag + * + * @param string + * @return string + */ + function form_close($extra = '') + { + return ''.$extra; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('form_prep')) +{ + /** + * Form Prep + * + * Formats text so that it can be safely placed in a form field in the event it has HTML tags. + * + * @deprecated 3.0.0 An alias for html_escape() + * @param string|string[] $str Value to escape + * @return string|string[] Escaped values + */ + function form_prep($str) + { + return html_escape($str, TRUE); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('set_value')) +{ + /** + * Form Value + * + * Grabs a value from the POST array for the specified field so you can + * re-populate an input field or textarea. If Form Validation + * is active it retrieves the info from the validation class + * + * @param string $field Field name + * @param string $default Default value + * @param bool $html_escape Whether to escape HTML special characters or not + * @return string + */ + function set_value($field, $default = '', $html_escape = TRUE) + { + $CI =& get_instance(); + + $value = (isset($CI->form_validation) && is_object($CI->form_validation) && $CI->form_validation->has_rule($field)) + ? $CI->form_validation->set_value($field, $default) + : $CI->input->post($field, FALSE); + + isset($value) OR $value = $default; + return ($html_escape) ? html_escape($value) : $value; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('set_select')) +{ + /** + * Set Select + * + * Let's you set the selected value of a + + + \ No newline at end of file diff --git a/ci-4.0-dev/application/config/Constants.php b/ci-4.0-dev/application/config/Constants.php new file mode 100644 index 000000000..9cfb22ced --- /dev/null +++ b/ci-4.0-dev/application/config/Constants.php @@ -0,0 +1,66 @@ +defaultNamespace() + * + * Modifies the namespace that is added to a controller if it doesn't + * already have one. By default this is the global namespace (\). + * + * $routes->defaultController() + * + * Changes the name of the class used as a controller when the route + * points to a folder instead of a class. + * + * $routes->defaultMethod() + * + * Assigns the method inside the controller that is ran when the + * Router is unable to determine the appropriate method to run. + * + * $routes->setAutoRoute() + * + * Determines whether the Router will attempt to match URIs to + * Controllers when no specific route has been defined. If false, + * only routes that have been defined here will be available. + */ +$routes->setDefaultNamespace('App\Controllers'); +$routes->setDefaultController('Home'); +$routes->setDefaultMethod('index'); +$routes->setTranslateURIDashes(false); +$routes->set404Override(); +$routes->setAutoRoute(true); + +/** + * -------------------------------------------------------------------- + * Route Definitions + * -------------------------------------------------------------------- + */ + +// We get a performance increase by specifying the default +// route since we don't have to scan directories. +$routes->add('/', 'Home::index'); + +/** + * -------------------------------------------------------------------- + * Additional Routing + * -------------------------------------------------------------------- + * + * There will often be times that you need additional routing and you + * need to it be able to override any defaults in this file. Environment + * based routes is one such time. require() additional route files here + * to make that happen. + * + * You will have access to the $routes object within that file without + * needing to reload it. + */ +if (file_exists(APPPATH.'Config/'.ENVIRONMENT.'/Routes.php')) +{ + require APPPATH.'Config/'.ENVIRONMENT.'/Routes.php'; +} diff --git a/ci-4.0-dev/application/config/Services.php b/ci-4.0-dev/application/config/Services.php new file mode 100644 index 000000000..543c2c4aa --- /dev/null +++ b/ci-4.0-dev/application/config/Services.php @@ -0,0 +1,34 @@ + + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + diff --git a/ci-4.0-dev/application/views/errors/cli/error_404.php b/ci-4.0-dev/application/views/errors/cli/error_404.php new file mode 100644 index 000000000..28a39b3ca --- /dev/null +++ b/ci-4.0-dev/application/views/errors/cli/error_404.php @@ -0,0 +1,8 @@ + + +An uncaught Exception was encountered + +Type: +Message: +Filename: getFile(), "\n"; ?> +Line Number: getLine(); ?> + + + + Backtrace: + getTrace() as $error): ?> + + + + + + diff --git a/ci-4.0-dev/application/views/errors/html/debug.css b/ci-4.0-dev/application/views/errors/html/debug.css new file mode 100644 index 000000000..e526b24fd --- /dev/null +++ b/ci-4.0-dev/application/views/errors/html/debug.css @@ -0,0 +1,176 @@ +body { + height: 100%; + background: #fafafa; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #777; + font-weight: 300; + margin: 0; + padding: 0; +} +h1 { + font-weight: lighter; + letter-spacing: 0.8; + font-size: 3rem; + color: #222; + margin: 0; +} +h1.headline { + margin-top: 20%; + font-size: 5rem; +} +.text-center { + text-align: center; +} +p.lead { + font-size: 1.6rem; +} +.container { + max-width: 75rem; + margin: 0 auto; + padding: 1rem; +} +.header { + background: #85271f; + color: #fff; +} +.header h1 { + color: #fff; +} +.header p { + font-size: 1.2rem; + margin: 0; + line-height: 2.5; +} +.header a { + color: rgba(255,255,255,0.5); + margin-left: 2rem; + display: none; + text-decoration: none; +} +.header:hover a { + display: inline; +} + +.footer .container { + border-top: 1px solid #e7e7e7; + margin-top: 1rem; + text-align: center; +} + +.source { + background: #333; + color: #c7c7c7; + padding: 0.5em 1em; + border-radius: 5px; + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + margin: 0; +} +.source span.line { + line-height: 1.4; +} +.source span.line .number { + color: #666; +} +.source .line .highlight { + display: block; + background: #555; + color: #fff; +} +.source span.highlight .number { + color: #fff; +} + +.tabs { + list-style: none; + list-style-position: inside; + margin: 0; + padding: 0; + margin-bottom: -1px; +} +.tabs li { + display: inline; +} +.tabs a:link, +.tabs a:visited { + padding: 0rem 1rem; + line-height: 2.7; + text-decoration: none; + color: #a7a7a7; + background: #f1f1f1; + border: 1px solid #e7e7e7; + border-bottom: 0; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + display: inline-block; +} +.tabs a:hover { + background: #e7e7e7; + border-color: #e1e1e1; +} +.tabs a.active { + background: #fff; +} +.tab-content { + background: #fff; + border: 1px solid #efefef; +} +.content { + padding: 1rem; +} +.hide { + display: none; +} + +.alert { + margin-top: 2rem; + display: block; + text-align: center; + line-height: 3.0; + background: #d9edf7; + border: 1px solid #bcdff1; + border-radius: 5px; + color: #31708f; +} +ul, ol { + line-height: 1.8; +} + +table { + width: 100%; + overflow: hidden; +} +th { + text-align: left; + border-bottom: 1px solid #e7e7e7; + padding-bottom: 0.5rem; +} +td { + padding: 0.2rem 0.5rem 0.2rem 0; +} +tr:hover td { + background: #f1f1f1; +} +td pre { + white-space: pre-wrap; +} + +.trace a { + color: inherit; +} +.trace table { + width: auto; +} +.trace tr td:first-child { + min-width: 5em; + font-weight: bold; +} +.trace td { + background: #e7e7e7; + padding: 0 1rem; +} +.trace td pre { + margin: 0; +} +.args { + display: none; +} \ No newline at end of file diff --git a/ci-4.0-dev/application/views/errors/html/debug.js b/ci-4.0-dev/application/views/errors/html/debug.js new file mode 100644 index 000000000..40a598d3c --- /dev/null +++ b/ci-4.0-dev/application/views/errors/html/debug.js @@ -0,0 +1,127 @@ +//-------------------------------------------------------------------- +// Tabs +//-------------------------------------------------------------------- + +var tabLinks = new Array(); +var contentDivs = new Array(); + +function init () +{ + + // Grab the tab links and content divs from the page + var tabListItems = document.getElementById('tabs').childNodes; + console.log(tabListItems); + for (var i = 0; i < tabListItems.length; i ++) + { + if (tabListItems[i].nodeName == "LI") + { + var tabLink = getFirstChildWithTagName(tabListItems[i], 'A'); + var id = getHash(tabLink.getAttribute('href')); + tabLinks[id] = tabLink; + contentDivs[id] = document.getElementById(id); + } + } + + // Assign onclick events to the tab links, and + // highlight the first tab + var i = 0; + + for (var id in tabLinks) + { + tabLinks[id].onclick = showTab; + tabLinks[id].onfocus = function () { this.blur() }; + if (i == 0) + { + tabLinks[id].className = 'active'; + } + i ++; + } + + // Hide all content divs except the first + var i = 0; + + for (var id in contentDivs) + { + if (i != 0) + { + console.log(contentDivs[id]); + contentDivs[id].className = 'content hide'; + } + i ++; + } +} + +//-------------------------------------------------------------------- + +function showTab () +{ + var selectedId = getHash(this.getAttribute('href')); + + // Highlight the selected tab, and dim all others. + // Also show the selected content div, and hide all others. + for (var id in contentDivs) + { + if (id == selectedId) + { + tabLinks[id].className = 'active'; + contentDivs[id].className = 'content'; + } + else + { + tabLinks[id].className = ''; + contentDivs[id].className = 'content hide'; + } + } + + // Stop the browser following the link + return false; +} + +//-------------------------------------------------------------------- + +function getFirstChildWithTagName (element, tagName) +{ + for (var i = 0; i < element.childNodes.length; i ++) + { + if (element.childNodes[i].nodeName == tagName) + { + return element.childNodes[i]; + } + } +} + +//-------------------------------------------------------------------- + +function getHash (url) +{ + var hashPos = url.lastIndexOf('#'); + return url.substring(hashPos + 1); +} + +//-------------------------------------------------------------------- + +function toggle (elem) +{ + elem = document.getElementById(elem); + + if (elem.style && elem.style['display']) + { + // Only works with the "style" attr + var disp = elem.style['display']; + } + else if (elem.currentStyle) + { + // For MSIE, naturally + var disp = elem.currentStyle['display']; + } + else if (window.getComputedStyle) + { + // For most other browsers + var disp = document.defaultView.getComputedStyle(elem, null).getPropertyValue('display'); + } + + // Toggle the state of the "display" style + elem.style.display = disp == 'block' ? 'none' : 'block'; + + return false; +} \ No newline at end of file diff --git a/ci-4.0-dev/application/views/errors/html/error_404.php b/ci-4.0-dev/application/views/errors/html/error_404.php new file mode 100644 index 000000000..ad3e12f66 --- /dev/null +++ b/ci-4.0-dev/application/views/errors/html/error_404.php @@ -0,0 +1,86 @@ + + + + + 404 Page Not Found + + + + +
+

404 - File Not Found

+ +

+ + + + Sorry! Cannot seem to find the page you were looking for. + +

+
+ + diff --git a/ci-4.0-dev/application/views/errors/html/error_exception.php b/ci-4.0-dev/application/views/errors/html/error_exception.php new file mode 100644 index 000000000..527dbadea --- /dev/null +++ b/ci-4.0-dev/application/views/errors/html/error_exception.php @@ -0,0 +1,386 @@ + + + + + + + + <?= htmlspecialchars($title, ENT_SUBSTITUTE, 'UTF-8') ?> + + + + + + + +
+ + +
+

at line

+ + +
+ +
+ +
+ +
+ + + +
+ + +
+ +
    + $row) : ?> + +
  1. +

    + + + + + {PHP internal code} + + + + +   —   + + + ( arguments ) +

    + + + $value) : ?> + getParameters(); + ?> + + + + + + +
    name : "#$key", ENT_SUBSTITUTE, 'UTF-8') ?>
    +
    + + () + + + + +   —   () + +

    + + + +
    + +
    + +
  2. + + +
+ +
+ + +
+ + + +

$

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + + '.print_r($value, true) ?> + +
+ + + + + + +

Constants

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + + '.print_r($value, true) ?> + +
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Pathuri ?>
HTTP MethodgetMethod(true) ?>
IP AddressgetIPAddress() ?>
Is AJAX Request?isAJAX() ? 'yes' : 'no' ?>
Is CLI Request?isCLI() ? 'yes' : 'no' ?>
Is Secure Request?isSecure() ? 'yes' : 'no' ?>
User AgentgetUserAgent() ?>
+ + + + + + + + +

$

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + + '.print_r($value, true) ?> + +
+ + + + + +
+ No $_GET, $_POST, or $_COOKIE Information to show. +
+ + + + getHeaders(); ?> + + +

Headers

+ + + + + + + + + + $value) : ?> + + + + + + + + + + +
HeaderValue
getName(), 'html') ?>getValueLine(), 'html') ?>
+ + +
+ + + setStatusCode(http_response_code()); + ?> +
+ + + + + +
Response StatusgetStatusCode().' - '.$response->getReason() ?>
+ + getHeaders(); ?> + + + +

Headers

+ + + + + + + + + + $value) : ?> + + + + + + +
HeaderValue
getHeaderLine($name), 'html') ?>
+ + +
+ + +
+ + +
    + +
  1. + +
+
+ + +
+ + + + + + + + + + + + + + + + +
Memory Usage
Peak Memory Usage:
Memory Limit:
+ +
+ +
+ +
+ + + + + diff --git a/ci-4.0-dev/application/views/errors/html/production.php b/ci-4.0-dev/application/views/errors/html/production.php new file mode 100644 index 000000000..bb9bfa5ca --- /dev/null +++ b/ci-4.0-dev/application/views/errors/html/production.php @@ -0,0 +1,25 @@ + + + + + + + Whoops! + + + + + +
+ +

Whoops!

+ +

We seem to have hit a snag. Please try again later...

+ +
+ + + + diff --git a/ci-4.0-dev/application/views/welcome_message.php b/ci-4.0-dev/application/views/welcome_message.php new file mode 100644 index 000000000..e8a576817 --- /dev/null +++ b/ci-4.0-dev/application/views/welcome_message.php @@ -0,0 +1,139 @@ + + + + Welcome to CodeIgniter + + + + + + + +
+ +

Welcome to CodeIgniter

+ +

version

+ + + +
+

The page you are looking at is being generated dynamically by CodeIgniter.

+ +

If you would like to edit this page you'll find it located at:

+ +
+				
+					application/Views/welcome_message.php
+				
+				
+ +

The corresponding controller for this page is found at:

+ +
+				
+					application/Controllers/Home.php
+				
+				
+ +

If you are exploring CodeIgniter for the very first time, you + should start by reading the (in progress) + User Guide.

+ +
+ + + +
+ + + diff --git a/ci-4.0-dev/composer.json b/ci-4.0-dev/composer.json new file mode 100644 index 000000000..896aa4fe1 --- /dev/null +++ b/ci-4.0-dev/composer.json @@ -0,0 +1,32 @@ +{ + "description": "The CodeIgniter framework v4", + "name": "codeigniter/framework", + "type": "project", + "homepage": "https://codeigniter.com", + "license": "MIT", + "support": { + "forum": "http://forum.codeigniter.com/", + "wiki": "https://github.com/bcit-ci/CodeIgniter4/wiki", + "irc": "irc://irc.freenode.net/codeigniter", + "source": "https://github.com/bcit-ci/CodeIgniter4" + }, + "autoload": { + "psr-4": { + "CodeIgniter\\": "system/" + } + }, + "require": { + "php": ">=7.0", + "zendframework/zend-escaper": "^2.5" + }, + "require-dev": { + "phpunit/phpunit": "5.3.*", + "mikey179/vfsStream": "1.6.*" + }, + "scripts": { + "post-update-cmd": [ + "composer dump-autoload", + "CodeIgniter\\ComposerScripts::postUpdate" + ] + } +} diff --git a/ci-4.0-dev/contributing.md b/ci-4.0-dev/contributing.md new file mode 100644 index 000000000..352f2eeba --- /dev/null +++ b/ci-4.0-dev/contributing.md @@ -0,0 +1,100 @@ +# Contributing to CodeIgniter4 + + +## Contributions (not) + +We are not accepting contributions from the public until a stable enough base has been formed, and our plans fleshed out and things settle down a little bit. +At that point, we will welcome your comments and help creating the best framework for our community. + +Once the repository is opened to the community, we expect all contributions to conform to our style guide, be commented (inside the PHP source files), +be documented (in the user guide), and unit tested (in the test folder). + +## Issues + +Issues are a quick way to point out a bug. If you find a bug or documentation error in CodeIgniter then please check a few things first: + +1. There is not already an open Issue +2. The issue has already been fixed (check the develop branch, or look for closed Issues) +3. Is it something really obvious that you can fix yourself? + +Reporting issues is helpful but an even better approach is to send a Pull Request, which is done by "Forking" the main repository and committing to your own copy. This will require you to use the version control system called Git. + +## Guidelines + +Before we look into how, here are the guidelines. If your Pull Requests fail +to pass these guidelines it will be declined and you will need to re-submit +when you’ve made the changes. This might sound a bit tough, but it is required +for us to maintain quality of the code-base. + +### PHP Style + +All code must meet the Style Guide, which will be an early part of the User Guide. +This makes certain that all code is the same format as the existing code and means it will be as readable as possible. + +### Documentation + +If you change anything that requires a change to documentation then you will need to add it. New classes, methods, parameters, changing default values, etc are all things that will require a change to documentation. The change-log must also be updated for every change. Also PHPDoc blocks must be maintained. + +### Compatibility + +CodeIgniter4 requires PHP 7. + +### Branching + +CodeIgniter4 uses the [Git-Flow](http://nvie.com/posts/a-successful-git-branching-model/) branching model which requires all pull requests to be sent to the "develop" branch. This is +where the next planned version will be developed. The "master" branch will always contain the latest stable version and is kept clean so a "hotfix" (e.g: an emergency security patch) can be applied to master to create a new version, without worrying about other features holding it up. For this reason all commits need to be made to "develop" and any sent to "master" will be closed automatically. If you have multiple changes to submit, please place all changes into their own branch on your fork. + +One thing at a time: A pull request should only contain one change. That does not mean only one commit, but one change - however many commits it took. The reason for this is that if you change X and Y but send a pull request for both at the same time, we might really want X but disagree with Y, meaning we cannot merge the request. Using the Git-Flow branching model you can create new branches for both of these features and send two requests. + +### Signing + +You must sign your work, certifying that you either wrote the work or otherwise have the right to pass it on to an open source project. git makes this trivial as you merely have to use `--signoff` on your commits to your CodeIgniter fork. + +`git commit --signoff` + +or simply + +`git commit -s` + +This will sign your commits with the information setup in your git config, e.g. + +`Signed-off-by: John Q Public ` + +If you are using [Tower](http://www.git-tower.com/) there is a "Sign-Off" checkbox in the commit window. You could even alias git commit to use the `-s` flag so you don’t have to think about it. + +By signing your work in this manner, you certify to a "Developer's Certificate of Origin". The current version of this certificate is in the `DCO.txt` file in the root of this repository. + + +## How-to Guide + +There are two ways to make changes, the easy way and the hard way. Either way you will need to [create a GitHub account](https://github.com/signup/free). + +Easy way GitHub allows in-line editing of files for making simple typo changes and quick-fixes. This is not the best way as you are unable to test the code works. If you do this you could be introducing syntax errors, etc, but for a Git-phobic user this is good for a quick-fix. + +Hard way The best way to contribute is to "clone" your fork of CodeIgniter to your development area. That sounds like some jargon, but "forking" on GitHub means "making a copy of that repo to your account" and "cloning" means "copying that code to your environment so you can work on it". + +1. Set up Git (Windows, Mac & Linux) +2. Go to the CodeIgniter repo +3. Fork it +4. Clone your CodeIgniter repo: git@github.com:/CodeIgniter.git +5. Checkout the "develop" branch At this point you are ready to start making changes. +6. Fix existing bugs on the Issue tracker after taking a look to see nobody else is working on them. +7. Commit the files +8. Push your develop branch to your fork +9. Send a pull request [http://help.github.com/send-pull-requests/](http://help.github.com/send-pull-requests/) + +The codebase maintainers will now be alerted about the change and at least one of the team will respond. If your change fails to meet the guidelines it will be bounced, or feedback will be provided to help you improve it. + +Once the maintainer handling your pull request is happy with it they will merge it into develop and your patch will be part of the next release. + +### Keeping your fork up-to-date + +Unlike systems like Subversion, Git can have multiple remotes. A remote is the name for a URL of a Git repository. By default your fork will have a remote named "origin" which points to your fork, but you can add another remote named "codeigniter" which points to `git://github.com/bcit-ci/CodeIgniter4.git`. This is a read-only remote but you can pull from this develop branch to update your own. + +If you are using command-line you can do the following: + +1. `git remote add codeigniter git://github.com/bcit-ci/CodeIgniter4.git` +2. `git pull codeigniter develop` +3. `git push origin develop` + +Now your fork is up to date. This should be done regularly, or before you send a pull request at least. \ No newline at end of file diff --git a/ci-4.0-dev/index.html b/ci-4.0-dev/index.html new file mode 100644 index 000000000..7e7cda4bb --- /dev/null +++ b/ci-4.0-dev/index.html @@ -0,0 +1,15 @@ + + + + CodeIgniter 4 ... almost! + + + + +

If you see this message, you have not configured your web server properly.

+

You need to set your "document root" to the public folder + inside your project. This could be your default setting, or that of + a virtual host, depending on how you set up your local development + environment.

+ + diff --git a/ci-4.0-dev/license.txt b/ci-4.0-dev/license.txt new file mode 100644 index 000000000..555b5e658 --- /dev/null +++ b/ci-4.0-dev/license.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 - 2016, British Columbia Institute of Technology + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ci-4.0-dev/phpcbf-fixed.diff b/ci-4.0-dev/phpcbf-fixed.diff new file mode 100644 index 000000000..9b325a41e --- /dev/null +++ b/ci-4.0-dev/phpcbf-fixed.diff @@ -0,0 +1,8093 @@ +--- vendor/symfony/polyfill-mbstring/bootstrap.php ++++ PHP_CodeSniffer +@@ -16,36 +16,132 @@ + define('MB_CASE_LOWER', 1); + define('MB_CASE_TITLE', 2); + +- function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); } +- function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); } +- function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); } +- function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); } +- function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); } +- function mb_language($lang = null) { return p\Mbstring::mb_language($lang); } +- function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } +- function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } +- function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); } +- function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); } +- function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); } +- function mb_parse_str($s, &$result = array()) { parse_str($s, $result); } +- function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); } +- function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); } +- function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); } +- function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); } +- function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); } +- function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); } +- function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); } +- function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); } +- function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); } +- function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); } +- function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); } +- function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); } +- function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); } +- function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } +- function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); } +- function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); } +- function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); } +- function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); } +- function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); } +- function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $v0, $a, $b, $c, $d, $e, $f); } ++ function mb_convert_encoding($s, $to, $from = null) ++ { ++ return p\Mbstring::mb_convert_encoding($s, $to, $from); ++ } ++ function mb_decode_mimeheader($s) ++ { ++ return p\Mbstring::mb_decode_mimeheader($s); ++ } ++ function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) ++ { ++ return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); ++ } ++ function mb_convert_case($s, $mode, $enc = null) ++ { ++ return p\Mbstring::mb_convert_case($s, $mode, $enc); ++ } ++ function mb_internal_encoding($enc = null) ++ { ++ return p\Mbstring::mb_internal_encoding($enc); ++ } ++ function mb_language($lang = null) ++ { ++ return p\Mbstring::mb_language($lang); ++ } ++ function mb_list_encodings() ++ { ++ return p\Mbstring::mb_list_encodings(); ++ } ++ function mb_encoding_aliases($encoding) ++ { ++ return p\Mbstring::mb_encoding_aliases($encoding); ++ } ++ function mb_check_encoding($var = null, $encoding = null) ++ { ++ return p\Mbstring::mb_check_encoding($var, $encoding); ++ } ++ function mb_detect_encoding($str, $encodingList = null, $strict = false) ++ { ++ return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); ++ } ++ function mb_detect_order($encodingList = null) ++ { ++ return p\Mbstring::mb_detect_order($encodingList); ++ } ++ function mb_parse_str($s, &$result = array()) ++ { ++ parse_str($s, $result); ++ } ++ function mb_strlen($s, $enc = null) ++ { ++ return p\Mbstring::mb_strlen($s, $enc); ++ } ++ function mb_strpos($s, $needle, $offset = 0, $enc = null) ++ { ++ return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); ++ } ++ function mb_strtolower($s, $enc = null) ++ { ++ return p\Mbstring::mb_strtolower($s, $enc); ++ } ++ function mb_strtoupper($s, $enc = null) ++ { ++ return p\Mbstring::mb_strtoupper($s, $enc); ++ } ++ function mb_substitute_character($char = null) ++ { ++ return p\Mbstring::mb_substitute_character($char); ++ } ++ function mb_substr($s, $start, $length = 2147483647, $enc = null) ++ { ++ return p\Mbstring::mb_substr($s, $start, $length, $enc); ++ } ++ function mb_stripos($s, $needle, $offset = 0, $enc = null) ++ { ++ return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); ++ } ++ function mb_stristr($s, $needle, $part = false, $enc = null) ++ { ++ return p\Mbstring::mb_stristr($s, $needle, $part, $enc); ++ } ++ function mb_strrchr($s, $needle, $part = false, $enc = null) ++ { ++ return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); ++ } ++ function mb_strrichr($s, $needle, $part = false, $enc = null) ++ { ++ return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); ++ } ++ function mb_strripos($s, $needle, $offset = 0, $enc = null) ++ { ++ return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); ++ } ++ function mb_strrpos($s, $needle, $offset = 0, $enc = null) ++ { ++ return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); ++ } ++ function mb_strstr($s, $needle, $part = false, $enc = null) ++ { ++ return p\Mbstring::mb_strstr($s, $needle, $part, $enc); ++ } ++ function mb_get_info($type = 'all') ++ { ++ return p\Mbstring::mb_get_info($type); ++ } ++ function mb_http_output($enc = null) ++ { ++ return p\Mbstring::mb_http_output($enc); ++ } ++ function mb_strwidth($s, $enc = null) ++ { ++ return p\Mbstring::mb_strwidth($s, $enc); ++ } ++ function mb_substr_count($haystack, $needle, $enc = null) ++ { ++ return p\Mbstring::mb_substr_count($haystack, $needle, $enc); ++ } ++ function mb_output_handler($contents, $status) ++ { ++ return p\Mbstring::mb_output_handler($contents, $status); ++ } ++ function mb_http_input($type = '') ++ { ++ return p\Mbstring::mb_http_input($type); ++ } ++ function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) ++ { ++ return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $v0, $a, $b, $c, $d, $e, $f); ++ } + } + +--- vendor/symfony/polyfill-mbstring/Mbstring.php ++++ PHP_CodeSniffer +@@ -115,11 +115,13 @@ + $vars = array(&$a, &$b, &$c, &$d, &$e, &$f); + + $ok = true; +- array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { +- if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { +- $ok = false; ++ array_walk_recursive( ++ $vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { ++ if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { ++ $ok = false; ++ } + } +- }); ++ ); + + return $ok ? $fromEncoding : false; + } +@@ -229,11 +231,11 @@ + } + + switch ($lang = strtolower($lang)) { +- case 'uni': +- case 'neutral': +- self::$language = $lang; ++ case 'uni': ++ case 'neutral': ++ self::$language = $lang; + +- return true; ++ return true; + } + + return false; +@@ -247,9 +249,9 @@ + public static function mb_encoding_aliases($encoding) + { + switch (strtoupper($encoding)) { +- case 'UTF8': +- case 'UTF-8': +- return array('utf8'); ++ case 'UTF8': ++ case 'UTF-8': ++ return array('utf8'); + } + + return false; +@@ -280,23 +282,23 @@ + + foreach ($encodingList as $enc) { + switch ($enc) { +- case 'ASCII': +- if (!preg_match('/[\x80-\xFF]/', $str)) { +- return $enc; +- } +- break; ++ case 'ASCII': ++ if (!preg_match('/[\x80-\xFF]/', $str)) { ++ return $enc; ++ } ++ break; + +- case 'UTF8': +- case 'UTF-8': +- if (preg_match('//u', $str)) { +- return 'UTF-8'; +- } +- break; ++ case 'UTF8': ++ case 'UTF-8': ++ if (preg_match('//u', $str)) { ++ return 'UTF-8'; ++ } ++ break; + +- default: +- if (0 === strncmp($enc, 'ISO-8859-', 9)) { +- return $enc; +- } ++ default: ++ if (0 === strncmp($enc, 'ISO-8859-', 9)) { ++ return $enc; ++ } + } + } + +@@ -316,13 +318,13 @@ + + foreach ($encodingList as $enc) { + switch ($enc) { +- default: +- if (strncmp($enc, 'ISO-8859-', 9)) { +- return false; +- } +- case 'ASCII': +- case 'UTF8': +- case 'UTF-8': ++ default: ++ if (strncmp($enc, 'ISO-8859-', 9)) { ++ return false; ++ } ++ case 'ASCII': ++ case 'UTF8': ++ case 'UTF-8': + } + } + +@@ -578,7 +580,7 @@ + private static function getData($file) + { + if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { +- return require $file; ++ return include $file; + } + + return false; + +--- vendor/symfony/translation/PluralizationRules.php ++++ PHP_CodeSniffer +@@ -55,135 +55,135 @@ + * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) + */ + switch ($locale) { +- case 'az': +- case 'bo': +- case 'dz': +- case 'id': +- case 'ja': +- case 'jv': +- case 'ka': +- case 'km': +- case 'kn': +- case 'ko': +- case 'ms': +- case 'th': +- case 'tr': +- case 'vi': +- case 'zh': +- return 0; ++ case 'az': ++ case 'bo': ++ case 'dz': ++ case 'id': ++ case 'ja': ++ case 'jv': ++ case 'ka': ++ case 'km': ++ case 'kn': ++ case 'ko': ++ case 'ms': ++ case 'th': ++ case 'tr': ++ case 'vi': ++ case 'zh': ++ return 0; + break; + +- case 'af': +- case 'bn': +- case 'bg': +- case 'ca': +- case 'da': +- case 'de': +- case 'el': +- case 'en': +- case 'eo': +- case 'es': +- case 'et': +- case 'eu': +- case 'fa': +- case 'fi': +- case 'fo': +- case 'fur': +- case 'fy': +- case 'gl': +- case 'gu': +- case 'ha': +- case 'he': +- case 'hu': +- case 'is': +- case 'it': +- case 'ku': +- case 'lb': +- case 'ml': +- case 'mn': +- case 'mr': +- case 'nah': +- case 'nb': +- case 'ne': +- case 'nl': +- case 'nn': +- case 'no': +- case 'om': +- case 'or': +- case 'pa': +- case 'pap': +- case 'ps': +- case 'pt': +- case 'so': +- case 'sq': +- case 'sv': +- case 'sw': +- case 'ta': +- case 'te': +- case 'tk': +- case 'ur': +- case 'zu': +- return ($number == 1) ? 0 : 1; +- +- case 'am': +- case 'bh': +- case 'fil': +- case 'fr': +- case 'gun': +- case 'hi': +- case 'hy': +- case 'ln': +- case 'mg': +- case 'nso': +- case 'xbr': +- case 'ti': +- case 'wa': +- return (($number == 0) || ($number == 1)) ? 0 : 1; +- +- case 'be': +- case 'bs': +- case 'hr': +- case 'ru': +- case 'sr': +- case 'uk': +- return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); +- +- case 'cs': +- case 'sk': +- return ($number == 1) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2); +- +- case 'ga': +- return ($number == 1) ? 0 : (($number == 2) ? 1 : 2); +- +- case 'lt': +- return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); +- +- case 'sl': +- return ($number % 100 == 1) ? 0 : (($number % 100 == 2) ? 1 : ((($number % 100 == 3) || ($number % 100 == 4)) ? 2 : 3)); +- +- case 'mk': +- return ($number % 10 == 1) ? 0 : 1; +- +- case 'mt': +- return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3)); +- +- case 'lv': +- return ($number == 0) ? 0 : ((($number % 10 == 1) && ($number % 100 != 11)) ? 1 : 2); +- +- case 'pl': +- return ($number == 1) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2); +- +- case 'cy': +- return ($number == 1) ? 0 : (($number == 2) ? 1 : ((($number == 8) || ($number == 11)) ? 2 : 3)); ++ case 'af': ++ case 'bn': ++ case 'bg': ++ case 'ca': ++ case 'da': ++ case 'de': ++ case 'el': ++ case 'en': ++ case 'eo': ++ case 'es': ++ case 'et': ++ case 'eu': ++ case 'fa': ++ case 'fi': ++ case 'fo': ++ case 'fur': ++ case 'fy': ++ case 'gl': ++ case 'gu': ++ case 'ha': ++ case 'he': ++ case 'hu': ++ case 'is': ++ case 'it': ++ case 'ku': ++ case 'lb': ++ case 'ml': ++ case 'mn': ++ case 'mr': ++ case 'nah': ++ case 'nb': ++ case 'ne': ++ case 'nl': ++ case 'nn': ++ case 'no': ++ case 'om': ++ case 'or': ++ case 'pa': ++ case 'pap': ++ case 'ps': ++ case 'pt': ++ case 'so': ++ case 'sq': ++ case 'sv': ++ case 'sw': ++ case 'ta': ++ case 'te': ++ case 'tk': ++ case 'ur': ++ case 'zu': ++ return ($number == 1) ? 0 : 1; ++ ++ case 'am': ++ case 'bh': ++ case 'fil': ++ case 'fr': ++ case 'gun': ++ case 'hi': ++ case 'hy': ++ case 'ln': ++ case 'mg': ++ case 'nso': ++ case 'xbr': ++ case 'ti': ++ case 'wa': ++ return (($number == 0) || ($number == 1)) ? 0 : 1; ++ ++ case 'be': ++ case 'bs': ++ case 'hr': ++ case 'ru': ++ case 'sr': ++ case 'uk': ++ return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); ++ ++ case 'cs': ++ case 'sk': ++ return ($number == 1) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2); ++ ++ case 'ga': ++ return ($number == 1) ? 0 : (($number == 2) ? 1 : 2); ++ ++ case 'lt': ++ return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); ++ ++ case 'sl': ++ return ($number % 100 == 1) ? 0 : (($number % 100 == 2) ? 1 : ((($number % 100 == 3) || ($number % 100 == 4)) ? 2 : 3)); ++ ++ case 'mk': ++ return ($number % 10 == 1) ? 0 : 1; ++ ++ case 'mt': ++ return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3)); ++ ++ case 'lv': ++ return ($number == 0) ? 0 : ((($number % 10 == 1) && ($number % 100 != 11)) ? 1 : 2); ++ ++ case 'pl': ++ return ($number == 1) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2); ++ ++ case 'cy': ++ return ($number == 1) ? 0 : (($number == 2) ? 1 : ((($number == 8) || ($number == 11)) ? 2 : 3)); + +- case 'ro': +- return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2); ++ case 'ro': ++ return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2); + +- case 'ar': +- return ($number == 0) ? 0 : (($number == 1) ? 1 : (($number == 2) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5)))); ++ case 'ar': ++ return ($number == 0) ? 0 : (($number == 1) ? 1 : (($number == 2) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5)))); + +- default: +- return 0; ++ default: ++ return 0; + } + } + + +--- vendor/symfony/translation/Dumper/IcuResFileDumper.php ++++ PHP_CodeSniffer +@@ -46,8 +46,7 @@ + + $data .= pack('V', strlen($target)) + .mb_convert_encoding($target."\0", 'UTF-16LE', 'UTF-8') +- .$this->writePadding($data) +- ; ++ .$this->writePadding($data); + } + + $resOffset = $this->getPosition($data); +@@ -60,7 +59,8 @@ + + $bundleTop = $this->getPosition($data); + +- $root = pack('V7', ++ $root = pack( ++ 'V7', + $resOffset + (2 << 28), // Resource Offset + Resource Type + 6, // Index length + $keyTop, // Index keys top +@@ -70,7 +70,8 @@ + 0 // Index attributes + ); + +- $header = pack('vC2v4C12@32', ++ $header = pack( ++ 'vC2v4C12@32', + 32, // Header size + 0xDA, 0x27, // Magic number 1 and 2 + 20, 0, 0, 2, // Rest of the header, ..., Size of a char + +--- vendor/symfony/translation/Dumper/FileDumper.php ++++ PHP_CodeSniffer +@@ -114,10 +114,12 @@ + */ + private function getRelativePath($domain, $locale) + { +- return strtr($this->relativePathTemplate, array( ++ return strtr( ++ $this->relativePathTemplate, array( + '%domain%' => $domain, + '%locale%' => $locale, + '%extension%' => $this->getExtension(), +- )); ++ ) ++ ); + } + } + +--- vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php ++++ PHP_CodeSniffer +@@ -140,8 +140,7 @@ + $translator = $this + ->getMockBuilder('Symfony\Component\Translation\DataCollectorTranslator') + ->disableOriginalConstructor() +- ->getMock() +- ; ++ ->getMock(); + + return $translator; + } + +--- vendor/symfony/translation/Tests/Dumper/CsvFileDumperTest.php ++++ PHP_CodeSniffer +@@ -19,8 +19,10 @@ + public function testFormatCatalogue() + { + $catalogue = new MessageCatalogue('en'); +- $catalogue->add(array('foo' => 'bar', 'bar' => 'foo +-foo', 'foo;foo' => 'bar')); ++ $catalogue->add( ++ array('foo' => 'bar', 'bar' => 'foo ++foo', 'foo;foo' => 'bar') ++ ); + + $dumper = new CsvFileDumper(); + + +--- vendor/symfony/translation/Tests/Dumper/YamlFileDumperTest.php ++++ PHP_CodeSniffer +@@ -23,11 +23,12 @@ + array( + 'foo.bar1' => 'value1', + 'foo.bar2' => 'value2', +- )); ++ ) ++ ); + +- $dumper = new YamlFileDumper(); ++ $dumper = new YamlFileDumper(); + +- $this->assertStringEqualsFile(__DIR__.'/../fixtures/messages.yml', $dumper->formatCatalogue($catalogue, 'messages', array('as_tree' => true, 'inline' => 999))); ++ $this->assertStringEqualsFile(__DIR__.'/../fixtures/messages.yml', $dumper->formatCatalogue($catalogue, 'messages', array('as_tree' => true, 'inline' => 999))); + } + + public function testLinearFormatCatalogue() +@@ -37,10 +38,11 @@ + array( + 'foo.bar1' => 'value1', + 'foo.bar2' => 'value2', +- )); ++ ) ++ ); + +- $dumper = new YamlFileDumper(); ++ $dumper = new YamlFileDumper(); + +- $this->assertStringEqualsFile(__DIR__.'/../fixtures/messages_linear.yml', $dumper->formatCatalogue($catalogue, 'messages')); ++ $this->assertStringEqualsFile(__DIR__.'/../fixtures/messages_linear.yml', $dumper->formatCatalogue($catalogue, 'messages')); + } + } + +--- vendor/symfony/translation/Tests/Dumper/XliffFileDumperTest.php ++++ PHP_CodeSniffer +@@ -19,11 +19,13 @@ + public function testFormatCatalogue() + { + $catalogue = new MessageCatalogue('en_US'); +- $catalogue->add(array( ++ $catalogue->add( ++ array( + 'foo' => 'bar', + 'key' => '', + 'key.with.cdata' => ' & ', +- )); ++ ) ++ ); + $catalogue->setMetadata('foo', array('notes' => array(array('priority' => 1, 'from' => 'bar', 'content' => 'baz')))); + $catalogue->setMetadata('key', array('notes' => array(array('content' => 'baz'), array('content' => 'qux')))); + +@@ -38,11 +40,13 @@ + public function testFormatCatalogueXliff2() + { + $catalogue = new MessageCatalogue('en_US'); +- $catalogue->add(array( ++ $catalogue->add( ++ array( + 'foo' => 'bar', + 'key' => '', + 'key.with.cdata' => ' & ', +- )); ++ ) ++ ); + $catalogue->setMetadata('key', array('target-attributes' => array('order' => 1))); + + $dumper = new XliffFileDumper(); +@@ -74,9 +78,11 @@ + public function testFormatCatalogueWithTargetAttributesMetadata() + { + $catalogue = new MessageCatalogue('en_US'); +- $catalogue->add(array( ++ $catalogue->add( ++ array( + 'foo' => 'bar', +- )); ++ ) ++ ); + $catalogue->setMetadata('foo', array('target-attributes' => array('state' => 'needs-translation'))); + + $dumper = new XliffFileDumper(); + +--- vendor/symfony/translation/Tests/Catalogue/MergeOperationTest.php ++++ PHP_CodeSniffer +@@ -43,9 +43,11 @@ + public function testGetResultFromSingleDomain() + { + $this->assertEquals( +- new MessageCatalogue('en', array( ++ new MessageCatalogue( ++ 'en', array( + 'messages' => array('a' => 'old_a', 'b' => 'old_b', 'c' => 'new_c'), +- )), ++ ) ++ ), + $this->createOperation( + new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))), + new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c'))) + +--- vendor/symfony/translation/Tests/Catalogue/TargetOperationTest.php ++++ PHP_CodeSniffer +@@ -43,9 +43,11 @@ + public function testGetResultFromSingleDomain() + { + $this->assertEquals( +- new MessageCatalogue('en', array( ++ new MessageCatalogue( ++ 'en', array( + 'messages' => array('a' => 'old_a', 'c' => 'new_c'), +- )), ++ ) ++ ), + $this->createOperation( + new MessageCatalogue('en', array('messages' => array('a' => 'old_a', 'b' => 'old_b'))), + new MessageCatalogue('en', array('messages' => array('a' => 'new_a', 'c' => 'new_c'))) + +--- vendor/symfony/translation/Tests/LoggingTranslatorTest.php ++++ PHP_CodeSniffer +@@ -22,8 +22,7 @@ + $logger = $this->getMock('Psr\Log\LoggerInterface'); + $logger->expects($this->exactly(2)) + ->method('warning') +- ->with('Translation not found.') +- ; ++ ->with('Translation not found.'); + + $translator = new Translator('ar'); + $loggableTranslator = new LoggingTranslator($translator, $logger); +@@ -36,8 +35,7 @@ + $logger = $this->getMock('Psr\Log\LoggerInterface'); + $logger->expects($this->once()) + ->method('debug') +- ->with('Translation use fallback catalogue.') +- ; ++ ->with('Translation use fallback catalogue.'); + + $translator = new Translator('ar'); + $translator->setFallbackLocales(array('en')); + +--- vendor/symfony/translation/Tests/TranslatorCacheTest.php ++++ PHP_CodeSniffer +@@ -94,13 +94,14 @@ + $catalogue = new MessageCatalogue($locale, array()); + $catalogue->addResource(new StaleResource()); // better use a helper class than a mock, because it gets serialized in the cache and re-loaded + +- /** @var LoaderInterface|\PHPUnit_Framework_MockObject_MockObject $loader */ ++ /** ++ * @var LoaderInterface|\PHPUnit_Framework_MockObject_MockObject $loader ++*/ + $loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface'); + $loader + ->expects($this->exactly(2)) + ->method('load') +- ->will($this->returnValue($catalogue)) +- ; ++ ->will($this->returnValue($catalogue)); + + // 1st pass + $translator = new Translator($locale, null, $this->tmpDir, true); + +--- vendor/symfony/translation/Loader/PhpFileLoader.php ++++ PHP_CodeSniffer +@@ -23,6 +23,6 @@ + */ + protected function loadResource($resource) + { +- return require $resource; ++ return include $resource; + } + } + +--- vendor/symfony/translation/Loader/JsonFileLoader.php ++++ PHP_CodeSniffer +@@ -47,18 +47,18 @@ + private function getJSONErrorMessage($errorCode) + { + switch ($errorCode) { +- case JSON_ERROR_DEPTH: +- return 'Maximum stack depth exceeded'; +- case JSON_ERROR_STATE_MISMATCH: +- return 'Underflow or the modes mismatch'; +- case JSON_ERROR_CTRL_CHAR: +- return 'Unexpected control character found'; +- case JSON_ERROR_SYNTAX: +- return 'Syntax error, malformed JSON'; +- case JSON_ERROR_UTF8: +- return 'Malformed UTF-8 characters, possibly incorrectly encoded'; +- default: +- return 'Unknown error'; ++ case JSON_ERROR_DEPTH: ++ return 'Maximum stack depth exceeded'; ++ case JSON_ERROR_STATE_MISMATCH: ++ return 'Underflow or the modes mismatch'; ++ case JSON_ERROR_CTRL_CHAR: ++ return 'Unexpected control character found'; ++ case JSON_ERROR_SYNTAX: ++ return 'Syntax error, malformed JSON'; ++ case JSON_ERROR_UTF8: ++ return 'Malformed UTF-8 characters, possibly incorrectly encoded'; ++ default: ++ return 'Unknown error'; + } + } + } + +--- vendor/symfony/translation/Loader/XliffFileLoader.php ++++ PHP_CodeSniffer +@@ -239,7 +239,8 @@ + { + $errors = array(); + foreach (libxml_get_errors() as $error) { +- $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)', ++ $errors[] = sprintf( ++ '[%s %s] %s (in %s - line %d, column %d)', + LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', + $error->code, + trim($error->message), +@@ -267,7 +268,9 @@ + */ + private function getVersionNumber(\DOMDocument $dom) + { +- /** @var \DOMNode $xliff */ ++ /** ++ * @var \DOMNode $xliff ++*/ + foreach ($dom->getElementsByTagName('xliff') as $xliff) { + $version = $xliff->attributes->getNamedItem('version'); + if ($version) { + +--- vendor/symfony/translation/Interval.php ++++ PHP_CodeSniffer +@@ -29,7 +29,7 @@ + * + * @author Fabien Potencier + * +- * @see http://en.wikipedia.org/wiki/Interval_%28mathematics%29#The_ISO_notation ++ * @see http://en.wikipedia.org/wiki/Interval_%28mathematics%29#The_ISO_notation + */ + class Interval + { + +--- vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php ++++ PHP_CodeSniffer +@@ -74,10 +74,14 @@ + } + + if (!isset($event['method'])) { +- $event['method'] = 'on'.preg_replace_callback(array( ++ $event['method'] = 'on'.preg_replace_callback( ++ array( + '/(?<=\b)[a-z]/i', + '/[^a-z0-9]/i', +- ), function ($matches) { return strtoupper($matches[0]); }, $event['event']); ++ ), function ($matches) { ++ return strtoupper($matches[0]); ++ }, $event['event'] ++ ); + $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']); + } + + +--- vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php ++++ PHP_CodeSniffer +@@ -72,7 +72,11 @@ + */ + public function testAddListenerDisallowed() + { +- $this->dispatcher->addListener('event', function () { return 'foo'; }); ++ $this->dispatcher->addListener( ++ 'event', function () { ++ return 'foo'; ++ } ++ ); + } + + /** +@@ -90,7 +94,11 @@ + */ + public function testRemoveListenerDisallowed() + { +- $this->dispatcher->removeListener('event', function () { return 'foo'; }); ++ $this->dispatcher->removeListener( ++ 'event', function () { ++ return 'foo'; ++ } ++ ); + } + + /** + +--- vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php ++++ PHP_CodeSniffer +@@ -25,7 +25,10 @@ + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + +- $tdispatcher->addListener('foo', $listener = function () {}); ++ $tdispatcher->addListener( ++ 'foo', $listener = function () { ++ } ++ ); + $listeners = $dispatcher->getListeners('foo'); + $this->assertCount(1, $listeners); + $this->assertSame($listener, $listeners[0]); +@@ -39,7 +42,10 @@ + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + +- $tdispatcher->addListener('foo', $listener = function () {}); ++ $tdispatcher->addListener( ++ 'foo', $listener = function () { ++ } ++ ); + $this->assertSame($dispatcher->getListeners('foo'), $tdispatcher->getListeners('foo')); + } + +@@ -51,7 +57,10 @@ + $this->assertFalse($dispatcher->hasListeners('foo')); + $this->assertFalse($tdispatcher->hasListeners('foo')); + +- $tdispatcher->addListener('foo', $listener = function () {}); ++ $tdispatcher->addListener( ++ 'foo', $listener = function () { ++ } ++ ); + $this->assertTrue($dispatcher->hasListeners('foo')); + $this->assertTrue($tdispatcher->hasListeners('foo')); + } +@@ -61,7 +70,10 @@ + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + +- $tdispatcher->addListener('foo', function () {}, 123); ++ $tdispatcher->addListener( ++ 'foo', function () { ++ }, 123 ++ ); + + $listeners = $dispatcher->getListeners('foo'); + $this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0])); +@@ -77,7 +89,10 @@ + { + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $traceableEventDispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); +- $traceableEventDispatcher->addListener('foo', function () {}, 123); ++ $traceableEventDispatcher->addListener( ++ 'foo', function () { ++ }, 123 ++ ); + $listeners = $traceableEventDispatcher->getListeners('foo'); + + $this->assertSame(0, $traceableEventDispatcher->getListenerPriority('foo', $listeners[0])); +@@ -103,7 +118,10 @@ + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); +- $tdispatcher->addListener('foo', $listener = function () {}); ++ $tdispatcher->addListener( ++ 'foo', $listener = function () { ++ } ++ ); + + $this->assertEquals(array(), $tdispatcher->getCalledListeners()); + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => 0)), $tdispatcher->getNotCalledListeners()); +@@ -118,11 +136,16 @@ + { + $tdispatcher = null; + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); +- $dispatcher->addListener('foo', function (Event $event, $eventName, $dispatcher) use (&$tdispatcher) { +- $tdispatcher = $dispatcher; +- $dispatcher->dispatch('bar'); +- }); +- $dispatcher->addListener('bar', function (Event $event) {}); ++ $dispatcher->addListener( ++ 'foo', function (Event $event, $eventName, $dispatcher) use (&$tdispatcher) { ++ $tdispatcher = $dispatcher; ++ $dispatcher->dispatch('bar'); ++ } ++ ); ++ $dispatcher->addListener( ++ 'bar', function (Event $event) { ++ } ++ ); + $dispatcher->dispatch('foo'); + $this->assertSame($dispatcher, $tdispatcher); + $this->assertCount(2, $dispatcher->getCalledListeners()); +@@ -134,8 +157,14 @@ + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger); +- $tdispatcher->addListener('foo', $listener1 = function () {}); +- $tdispatcher->addListener('foo', $listener2 = function () {}); ++ $tdispatcher->addListener( ++ 'foo', $listener1 = function () { ++ } ++ ); ++ $tdispatcher->addListener( ++ 'foo', $listener2 = function () { ++ } ++ ); + + $logger->expects($this->at(0))->method('debug')->with('Notified event "foo" to listener "closure".'); + $logger->expects($this->at(1))->method('debug')->with('Notified event "foo" to listener "closure".'); +@@ -149,8 +178,15 @@ + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger); +- $tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); }); +- $tdispatcher->addListener('foo', $listener2 = function () {}); ++ $tdispatcher->addListener( ++ 'foo', $listener1 = function (Event $event) { ++ $event->stopPropagation(); ++ } ++ ); ++ $tdispatcher->addListener( ++ 'foo', $listener2 = function () { ++ } ++ ); + + $logger->expects($this->at(0))->method('debug')->with('Notified event "foo" to listener "closure".'); + $logger->expects($this->at(1))->method('debug')->with('Listener "closure" stopped propagation of the event "foo".'); +@@ -165,8 +201,16 @@ + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); +- $tdispatcher->addListener('foo', function () use (&$called) { $called[] = 'foo1'; }, 10); +- $tdispatcher->addListener('foo', function () use (&$called) { $called[] = 'foo2'; }, 20); ++ $tdispatcher->addListener( ++ 'foo', function () use (&$called) { ++ $called[] = 'foo1'; ++ }, 10 ++ ); ++ $tdispatcher->addListener( ++ 'foo', function () use (&$called) { ++ $called[] = 'foo2'; ++ }, 20 ++ ); + + $tdispatcher->dispatch('foo'); + +@@ -177,12 +221,14 @@ + { + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $loop = 1; +- $dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) { +- ++$loop; +- if (2 == $loop) { +- $dispatcher->dispatch('foo'); ++ $dispatcher->addListener( ++ 'foo', $listener1 = function () use ($dispatcher, &$loop) { ++ ++$loop; ++ if (2 == $loop) { ++ $dispatcher->dispatch('foo'); ++ } + } +- }); ++ ); + + $dispatcher->dispatch('foo'); + } +@@ -191,12 +237,16 @@ + { + $nestedCall = false; + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); +- $dispatcher->addListener('foo', function (Event $e) use ($dispatcher) { +- $dispatcher->dispatch('bar', $e); +- }); +- $dispatcher->addListener('bar', function (Event $e) use (&$nestedCall) { +- $nestedCall = true; +- }); ++ $dispatcher->addListener( ++ 'foo', function (Event $e) use ($dispatcher) { ++ $dispatcher->dispatch('bar', $e); ++ } ++ ); ++ $dispatcher->addListener( ++ 'bar', function (Event $e) use (&$nestedCall) { ++ $nestedCall = true; ++ } ++ ); + + $this->assertFalse($nestedCall); + $dispatcher->dispatch('foo'); +@@ -210,7 +260,10 @@ + $dispatcher->removeListener('foo', $listener1); + }; + $eventDispatcher->addListener('foo', $listener1); +- $eventDispatcher->addListener('foo', function () {}); ++ $eventDispatcher->addListener( ++ 'foo', function () { ++ } ++ ); + $eventDispatcher->dispatch('foo'); + + $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed'); + +--- vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php ++++ PHP_CodeSniffer +@@ -35,8 +35,7 @@ + $service + ->expects($this->once()) + ->method('onEvent') +- ->with($event) +- ; ++ ->with($event); + + $container = new Container(); + $container->set('service.listener', $service); +@@ -56,20 +55,17 @@ + $service + ->expects($this->once()) + ->method('onEvent') +- ->with($event) +- ; ++ ->with($event); + + $service + ->expects($this->once()) + ->method('onEventWithPriority') +- ->with($event) +- ; ++ ->with($event); + + $service + ->expects($this->once()) + ->method('onEventNested') +- ->with($event) +- ; ++ ->with($event); + + $container = new Container(); + $container->set('service.subscriber', $service); +@@ -91,8 +87,7 @@ + $service + ->expects($this->once()) + ->method('onEvent') +- ->with($event) +- ; ++ ->with($event); + + $container = new Container(); + $container->set('service.listener', $service); +@@ -138,8 +133,7 @@ + $service1 + ->expects($this->exactly(2)) + ->method('onEvent') +- ->with($event) +- ; ++ ->with($event); + + $scope = new Scope('scope'); + $container = new Container(); +@@ -157,8 +151,7 @@ + $service2 + ->expects($this->once()) + ->method('onEvent') +- ->with($event) +- ; ++ ->with($event); + + $container->enterScope('scope'); + $container->set('service.listener', $service2, 'scope'); +@@ -188,8 +181,7 @@ + $service + ->expects($this->once()) + ->method('onEvent') +- ->with($event) +- ; ++ ->with($event); + + $this->assertTrue($dispatcher->hasListeners()); + + +--- vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php ++++ PHP_CodeSniffer +@@ -119,7 +119,12 @@ + $this->assertSame(-10, $this->dispatcher->getListenerPriority('pre.foo', $listener1)); + $this->assertSame(0, $this->dispatcher->getListenerPriority('pre.foo', $listener2)); + $this->assertNull($this->dispatcher->getListenerPriority('pre.bar', $listener2)); +- $this->assertNull($this->dispatcher->getListenerPriority('pre.foo', function () {})); ++ $this->assertNull( ++ $this->dispatcher->getListenerPriority( ++ 'pre.foo', function () { ++ } ++ ) ++ ); + } + + public function testDispatch() +@@ -269,9 +274,11 @@ + public function testLegacyEventReceivesTheDispatcherInstance() + { + $dispatcher = null; +- $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) { +- $dispatcher = $event->getDispatcher(); +- }); ++ $this->dispatcher->addListener( ++ 'test', function ($event) use (&$dispatcher) { ++ $dispatcher = $event->getDispatcher(); ++ } ++ ); + $this->dispatcher->dispatch('test'); + $this->assertSame($this->dispatcher, $dispatcher); + } +@@ -299,13 +306,17 @@ + { + $dispatcher = $this->createEventDispatcher(); + $dispatcher->addListener('bug.62976', new CallableClass()); +- $dispatcher->removeListener('bug.62976', function () {}); ++ $dispatcher->removeListener( ++ 'bug.62976', function () { ++ } ++ ); + $this->assertTrue($dispatcher->hasListeners('bug.62976')); + } + + public function testHasListenersWhenAddedCallbackListenerIsRemoved() + { +- $listener = function () {}; ++ $listener = function () { ++ }; + $this->dispatcher->addListener('foo', $listener); + $this->dispatcher->removeListener('foo', $listener); + $this->assertFalse($this->dispatcher->hasListeners()); +@@ -313,7 +324,8 @@ + + public function testGetListenersWhenAddedCallbackListenerIsRemoved() + { +- $listener = function () {}; ++ $listener = function () { ++ }; + $this->dispatcher->addListener('foo', $listener); + $this->dispatcher->removeListener('foo', $listener); + $this->assertSame(array(), $this->dispatcher->getListeners()); + +--- vendor/symfony/validator/PropertyMetadataInterface.php ++++ PHP_CodeSniffer +@@ -22,7 +22,7 @@ + * + * @author Bernhard Schussek + * +- * @see MetadataInterface ++ * @see MetadataInterface + * @deprecated since version 2.5, to be removed in 3.0. + * Use {@link Mapping\PropertyMetadataInterface} instead. + */ + +--- vendor/symfony/validator/Context/ExecutionContextInterface.php ++++ PHP_CodeSniffer +@@ -56,7 +56,7 @@ + * cannot store a context and expect that the methods still return the same + * results later on. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + */ + +--- vendor/symfony/validator/Context/ExecutionContextFactoryInterface.php ++++ PHP_CodeSniffer +@@ -19,7 +19,7 @@ + * You can use a custom factory if you want to customize the execution context + * that is passed through the validation run. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + */ + +--- vendor/symfony/validator/Context/ExecutionContext.php ++++ PHP_CodeSniffer +@@ -27,7 +27,7 @@ + /** + * The context used and created by {@link ExecutionContextFactory}. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + * +@@ -196,23 +196,24 @@ + ->setInvalidValue($invalidValue) + ->setPlural($plural) + ->setCode($code) +- ->addViolation() +- ; ++ ->addViolation(); + + return; + } + +- $this->violations->add(new ConstraintViolation( +- $this->translator->trans($message, $parameters, $this->translationDomain), +- $message, +- $parameters, +- $this->root, +- $this->propertyPath, +- $this->value, +- null, +- null, +- $this->constraint +- )); ++ $this->violations->add( ++ new ConstraintViolation( ++ $this->translator->trans($message, $parameters, $this->translationDomain), ++ $message, ++ $parameters, ++ $this->root, ++ $this->propertyPath, ++ $this->value, ++ null, ++ null, ++ $this->constraint ++ ) ++ ); + } + + /** +@@ -327,8 +328,7 @@ + ->setInvalidValue($invalidValue) + ->setPlural($plural) + ->setCode($code) +- ->addViolation() +- ; ++ ->addViolation(); + + return; + } +@@ -336,8 +336,7 @@ + $this + ->buildViolation($message, $parameters) + ->atPath($subPath) +- ->addViolation() +- ; ++ ->addViolation(); + } + + /** +@@ -355,8 +354,7 @@ + ->getValidator() + ->inContext($this) + ->atPath($subPath) +- ->validate($value, $constraint, $groups) +- ; ++ ->validate($value, $constraint, $groups); + } + + if ($traverse && $value instanceof \Traversable) { +@@ -366,16 +364,14 @@ + ->getValidator() + ->inContext($this) + ->atPath($subPath) +- ->validate($value, $constraint, $groups) +- ; ++ ->validate($value, $constraint, $groups); + } + + return $this + ->getValidator() + ->inContext($this) + ->atPath($subPath) +- ->validate($value, null, $groups) +- ; ++ ->validate($value, null, $groups); + } + + /** +@@ -389,8 +385,7 @@ + ->getValidator() + ->inContext($this) + ->atPath($subPath) +- ->validate($value, $constraints, $groups) +- ; ++ ->validate($value, $constraints, $groups); + } + + /** + +--- vendor/symfony/validator/Context/ExecutionContextFactory.php ++++ PHP_CodeSniffer +@@ -17,7 +17,7 @@ + /** + * Creates new {@link ExecutionContext} instances. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + * + +--- vendor/symfony/validator/Context/LegacyExecutionContextFactory.php ++++ PHP_CodeSniffer +@@ -22,7 +22,7 @@ + * + * Implemented for backward compatibility with Symfony < 2.5. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + * + +--- vendor/symfony/validator/Context/LegacyExecutionContext.php ++++ PHP_CodeSniffer +@@ -20,7 +20,7 @@ + /** + * An execution context that is compatible with the legacy API (< 2.5). + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + * + +--- vendor/symfony/validator/Violation/ConstraintViolationBuilder.php ++++ PHP_CodeSniffer +@@ -20,7 +20,7 @@ + /** + * Default implementation of {@link ConstraintViolationBuilderInterface}. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + * +@@ -199,7 +199,7 @@ + $this->message, + $this->plural, + $this->parameters, +- $this->translationDomain# ++ $this->translationDomain// + ); + } catch (\InvalidArgumentException $e) { + $translatedMessage = $this->translator->trans( +@@ -210,17 +210,19 @@ + } + } + +- $this->violations->add(new ConstraintViolation( +- $translatedMessage, +- $this->message, +- $this->parameters, +- $this->root, +- $this->propertyPath, +- $this->invalidValue, +- $this->plural, +- $this->code, +- $this->constraint, +- $this->cause +- )); ++ $this->violations->add( ++ new ConstraintViolation( ++ $translatedMessage, ++ $this->message, ++ $this->parameters, ++ $this->root, ++ $this->propertyPath, ++ $this->invalidValue, ++ $this->plural, ++ $this->code, ++ $this->constraint, ++ $this->cause ++ ) ++ ); + } + } + +--- vendor/symfony/validator/Violation/ConstraintViolationBuilderInterface.php ++++ PHP_CodeSniffer +@@ -19,7 +19,7 @@ + * Finally, call {@link addViolation()} to add the violation to the current + * execution context. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + */ + +--- vendor/symfony/validator/Tests/ConstraintViolationListTest.php ++++ PHP_CodeSniffer +@@ -102,13 +102,15 @@ + + public function testToString() + { +- $this->list = new ConstraintViolationList(array( ++ $this->list = new ConstraintViolationList( ++ array( + $this->getViolation('Error 1', 'Root'), + $this->getViolation('Error 2', 'Root', 'foo.bar'), + $this->getViolation('Error 3', 'Root', '[baz]'), + $this->getViolation('Error 4', '', 'foo.bar'), + $this->getViolation('Error 5', '', '[baz]'), +- )); ++ ) ++ ); + + $expected = <<<'EOF' + Root: + +--- vendor/symfony/validator/Tests/ConstraintTest.php ++++ PHP_CodeSniffer +@@ -23,10 +23,12 @@ + { + public function testSetProperties() + { +- $constraint = new ConstraintA(array( ++ $constraint = new ConstraintA( ++ array( + 'property1' => 'foo', + 'property2' => 'bar', +- )); ++ ) ++ ); + + $this->assertEquals('foo', $constraint->property1); + $this->assertEquals('bar', $constraint->property2); +@@ -36,9 +38,11 @@ + { + $this->setExpectedException('Symfony\Component\Validator\Exception\InvalidOptionsException'); + +- new ConstraintA(array( ++ new ConstraintA( ++ array( + 'foo' => 'bar', +- )); ++ ) ++ ); + } + + public function testMagicPropertiesAreNotAllowed() +@@ -54,10 +58,12 @@ + { + $this->setExpectedException('Symfony\Component\Validator\Exception\InvalidOptionsException'); + +- new ConstraintC(array( ++ new ConstraintC( ++ array( + 'option1' => 'default', + 'foo' => 'bar', +- )); ++ ) ++ ); + } + + public function testSetDefaultProperty() +@@ -158,10 +164,12 @@ + + public function testSerialize() + { +- $constraint = new ConstraintA(array( ++ $constraint = new ConstraintA( ++ array( + 'property1' => 'foo', + 'property2' => 'bar', +- )); ++ ) ++ ); + + $restoredConstraint = unserialize(serialize($constraint)); + +@@ -170,29 +178,35 @@ + + public function testSerializeInitializesGroupsOptionToDefault() + { +- $constraint = new ConstraintA(array( ++ $constraint = new ConstraintA( ++ array( + 'property1' => 'foo', + 'property2' => 'bar', +- )); ++ ) ++ ); + + $constraint = unserialize(serialize($constraint)); + +- $expected = new ConstraintA(array( ++ $expected = new ConstraintA( ++ array( + 'property1' => 'foo', + 'property2' => 'bar', + 'groups' => 'Default', +- )); ++ ) ++ ); + + $this->assertEquals($expected, $constraint); + } + + public function testSerializeKeepsCustomGroups() + { +- $constraint = new ConstraintA(array( ++ $constraint = new ConstraintA( ++ array( + 'property1' => 'foo', + 'property2' => 'bar', + 'groups' => 'MyGroup', +- )); ++ ) ++ ); + + $constraint = unserialize(serialize($constraint)); + + +--- vendor/symfony/validator/Tests/Constraints/TimeValidatorTest.php ++++ PHP_CodeSniffer +@@ -80,9 +80,11 @@ + */ + public function testInvalidTimes($time, $code) + { +- $constraint = new Time(array( ++ $constraint = new Time( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($time, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/DateTimeValidatorTest.php ++++ PHP_CodeSniffer +@@ -80,9 +80,11 @@ + */ + public function testInvalidDateTimes($dateTime, $code) + { +- $constraint = new DateTime(array( ++ $constraint = new DateTime( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($dateTime, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/CompositeTest.php ++++ PHP_CodeSniffer +@@ -40,10 +40,12 @@ + { + public function testMergeNestedGroupsIfNoExplicitParentGroup() + { +- $constraint = new ConcreteComposite(array( ++ $constraint = new ConcreteComposite( ++ array( + new NotNull(array('groups' => 'Default')), + new NotBlank(array('groups' => array('Default', 'Strict'))), +- )); ++ ) ++ ); + + $this->assertEquals(array('Default', 'Strict'), $constraint->groups); + $this->assertEquals(array('Default'), $constraint->constraints[0]->groups); +@@ -52,13 +54,15 @@ + + public function testSetImplicitNestedGroupsIfExplicitParentGroup() + { +- $constraint = new ConcreteComposite(array( ++ $constraint = new ConcreteComposite( ++ array( + 'constraints' => array( + new NotNull(), + new NotBlank(), + ), + 'groups' => array('Default', 'Strict'), +- )); ++ ) ++ ); + + $this->assertEquals(array('Default', 'Strict'), $constraint->groups); + $this->assertEquals(array('Default', 'Strict'), $constraint->constraints[0]->groups); +@@ -67,13 +71,15 @@ + + public function testExplicitNestedGroupsMustBeSubsetOfExplicitParentGroups() + { +- $constraint = new ConcreteComposite(array( ++ $constraint = new ConcreteComposite( ++ array( + 'constraints' => array( + new NotNull(array('groups' => 'Default')), + new NotBlank(array('groups' => 'Strict')), + ), + 'groups' => array('Default', 'Strict'), +- )); ++ ) ++ ); + + $this->assertEquals(array('Default', 'Strict'), $constraint->groups); + $this->assertEquals(array('Default'), $constraint->constraints[0]->groups); +@@ -85,20 +91,24 @@ + */ + public function testFailIfExplicitNestedGroupsNotSubsetOfExplicitParentGroups() + { +- new ConcreteComposite(array( ++ new ConcreteComposite( ++ array( + 'constraints' => array( + new NotNull(array('groups' => array('Default', 'Foobar'))), + ), + 'groups' => array('Default', 'Strict'), +- )); ++ ) ++ ); + } + + public function testImplicitGroupNamesAreForwarded() + { +- $constraint = new ConcreteComposite(array( ++ $constraint = new ConcreteComposite( ++ array( + new NotNull(array('groups' => 'Default')), + new NotBlank(array('groups' => 'Strict')), +- )); ++ ) ++ ); + + $constraint->addImplicitGroupName('ImplicitGroup'); + +@@ -120,10 +130,12 @@ + */ + public function testFailIfNoConstraint() + { +- new ConcreteComposite(array( ++ new ConcreteComposite( ++ array( + new NotNull(array('groups' => 'Default')), + 'NotBlank', +- )); ++ ) ++ ); + } + + /** +@@ -131,8 +143,10 @@ + */ + public function testValidCantBeNested() + { +- new ConcreteComposite(array( ++ new ConcreteComposite( ++ array( + new Valid(), +- )); ++ ) ++ ); + } + } + +--- vendor/symfony/validator/Tests/Constraints/ImageValidatorTest.php ++++ PHP_CodeSniffer +@@ -77,9 +77,11 @@ + public function testFileNotFound() + { + // Check that the logic from FileValidator still works +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'notFoundMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate('foobar', $constraint); + +@@ -91,12 +93,14 @@ + + public function testValidSize() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'minWidth' => 1, + 'maxWidth' => 2, + 'minHeight' => 1, + 'maxHeight' => 2, +- )); ++ ) ++ ); + + $this->validator->validate($this->image, $constraint); + +@@ -105,10 +109,12 @@ + + public function testWidthTooSmall() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'minWidth' => 3, + 'minWidthMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($this->image, $constraint); + +@@ -121,10 +127,12 @@ + + public function testWidthTooBig() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'maxWidth' => 1, + 'maxWidthMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($this->image, $constraint); + +@@ -137,10 +145,12 @@ + + public function testHeightTooSmall() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'minHeight' => 3, + 'minHeightMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($this->image, $constraint); + +@@ -153,10 +163,12 @@ + + public function testHeightTooBig() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'maxHeight' => 1, + 'maxHeightMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($this->image, $constraint); + +@@ -172,9 +184,11 @@ + */ + public function testInvalidMinWidth() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'minWidth' => '1abc', +- )); ++ ) ++ ); + + $this->validator->validate($this->image, $constraint); + } +@@ -184,9 +198,11 @@ + */ + public function testInvalidMaxWidth() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'maxWidth' => '1abc', +- )); ++ ) ++ ); + + $this->validator->validate($this->image, $constraint); + } +@@ -196,9 +212,11 @@ + */ + public function testInvalidMinHeight() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'minHeight' => '1abc', +- )); ++ ) ++ ); + + $this->validator->validate($this->image, $constraint); + } +@@ -208,19 +226,23 @@ + */ + public function testInvalidMaxHeight() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'maxHeight' => '1abc', +- )); ++ ) ++ ); + + $this->validator->validate($this->image, $constraint); + } + + public function testRatioTooSmall() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'minRatio' => 2, + 'minRatioMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($this->image, $constraint); + +@@ -233,10 +255,12 @@ + + public function testRatioTooBig() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'maxRatio' => 0.5, + 'maxRatioMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($this->image, $constraint); + +@@ -249,9 +273,11 @@ + + public function testMaxRatioUsesTwoDecimalsOnly() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'maxRatio' => 1.33, +- )); ++ ) ++ ); + + $this->validator->validate($this->image4By3, $constraint); + +@@ -263,9 +289,11 @@ + */ + public function testInvalidMinRatio() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'minRatio' => '1abc', +- )); ++ ) ++ ); + + $this->validator->validate($this->image, $constraint); + } +@@ -275,19 +303,23 @@ + */ + public function testInvalidMaxRatio() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'maxRatio' => '1abc', +- )); ++ ) ++ ); + + $this->validator->validate($this->image, $constraint); + } + + public function testSquareNotAllowed() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'allowSquare' => false, + 'allowSquareMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($this->image, $constraint); + +@@ -300,10 +332,12 @@ + + public function testLandscapeNotAllowed() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'allowLandscape' => false, + 'allowLandscapeMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($this->imageLandscape, $constraint); + +@@ -316,10 +350,12 @@ + + public function testPortraitNotAllowed() + { +- $constraint = new Image(array( ++ $constraint = new Image( ++ array( + 'allowPortrait' => false, + 'allowPortraitMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($this->imagePortrait, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/LengthValidatorTest.php ++++ PHP_CodeSniffer +@@ -135,10 +135,12 @@ + */ + public function testInvalidValuesMin($value) + { +- $constraint = new Length(array( ++ $constraint = new Length( ++ array( + 'min' => 4, + 'minMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -156,10 +158,12 @@ + */ + public function testInvalidValuesMax($value) + { +- $constraint = new Length(array( ++ $constraint = new Length( ++ array( + 'max' => 4, + 'maxMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -177,11 +181,13 @@ + */ + public function testInvalidValuesExactLessThanFour($value) + { +- $constraint = new Length(array( ++ $constraint = new Length( ++ array( + 'min' => 4, + 'max' => 4, + 'exactMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -199,11 +205,13 @@ + */ + public function testInvalidValuesExactMoreThanFour($value) + { +- $constraint = new Length(array( ++ $constraint = new Length( ++ array( + 'min' => 4, + 'max' => 4, + 'exactMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -221,12 +229,14 @@ + */ + public function testOneCharset($value, $charset, $isValid) + { +- $constraint = new Length(array( ++ $constraint = new Length( ++ array( + 'min' => 1, + 'max' => 1, + 'charset' => $charset, + 'charsetMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/AbstractConstraintValidatorTest.php ++++ PHP_CodeSniffer +@@ -101,24 +101,24 @@ + $contextualValidator = $this->getMock('Symfony\Component\Validator\Validator\ContextualValidatorInterface'); + + switch ($this->getApiVersion()) { +- case Validation::API_VERSION_2_5: +- $context = new ExecutionContext( +- $validator, +- $this->root, +- $translator +- ); +- break; +- case Validation::API_VERSION_2_4: +- case Validation::API_VERSION_2_5_BC: +- $context = new LegacyExecutionContext( +- $validator, +- $this->root, +- $this->getMock('Symfony\Component\Validator\MetadataFactoryInterface'), +- $translator +- ); +- break; +- default: +- throw new \RuntimeException('Invalid API version'); ++ case Validation::API_VERSION_2_5: ++ $context = new ExecutionContext( ++ $validator, ++ $this->root, ++ $translator ++ ); ++ break; ++ case Validation::API_VERSION_2_4: ++ case Validation::API_VERSION_2_5_BC: ++ $context = new LegacyExecutionContext( ++ $validator, ++ $this->root, ++ $this->getMock('Symfony\Component\Validator\MetadataFactoryInterface'), ++ $translator ++ ); ++ break; ++ default: ++ throw new \RuntimeException('Invalid API version'); + } + + $context->setGroup($this->group); + +--- vendor/symfony/validator/Tests/Constraints/NotNullValidatorTest.php ++++ PHP_CodeSniffer +@@ -49,9 +49,11 @@ + + public function testNullIsInvalid() + { +- $constraint = new NotNull(array( ++ $constraint = new NotNull( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate(null, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/CollectionValidatorTest.php ++++ PHP_CodeSniffer +@@ -35,9 +35,13 @@ + + public function testNullIsValid() + { +- $this->validator->validate(null, new Collection(array('fields' => array( +- 'foo' => new Range(array('min' => 4)), +- )))); ++ $this->validator->validate( ++ null, new Collection( ++ array('fields' => array( ++ 'foo' => new Range(array('min' => 4)), ++ )) ++ ) ++ ); + + $this->assertNoViolation(); + } +@@ -50,9 +54,13 @@ + + $this->expectValidateValueAt(0, '[foo]', $data['foo'], array($constraint)); + +- $this->validator->validate($data, new Collection(array( +- 'foo' => $constraint, +- ))); ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'foo' => $constraint, ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } +@@ -62,9 +70,13 @@ + */ + public function testThrowsExceptionIfNotTraversable() + { +- $this->validator->validate('foobar', new Collection(array('fields' => array( +- 'foo' => new Range(array('min' => 4)), +- )))); ++ $this->validator->validate( ++ 'foobar', new Collection( ++ array('fields' => array( ++ 'foo' => new Range(array('min' => 4)), ++ )) ++ ) ++ ); + } + + public function testWalkSingleConstraint() +@@ -84,12 +96,16 @@ + + $data = $this->prepareTestData($array); + +- $this->validator->validate($data, new Collection(array( +- 'fields' => array( ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'fields' => array( + 'foo' => $constraint, + 'bar' => $constraint, +- ), +- ))); ++ ), ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } +@@ -114,12 +130,16 @@ + + $data = $this->prepareTestData($array); + +- $this->validator->validate($data, new Collection(array( +- 'fields' => array( ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'fields' => array( + 'foo' => $constraints, + 'bar' => $constraints, +- ), +- ))); ++ ), ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } +@@ -128,19 +148,25 @@ + { + $constraint = new Range(array('min' => 4)); + +- $data = $this->prepareTestData(array( ++ $data = $this->prepareTestData( ++ array( + 'foo' => 5, + 'baz' => 6, +- )); ++ ) ++ ); + + $this->expectValidateValueAt(0, '[foo]', $data['foo'], array($constraint)); + +- $this->validator->validate($data, new Collection(array( +- 'fields' => array( ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'fields' => array( + 'foo' => $constraint, +- ), +- 'extraFieldsMessage' => 'myMessage', +- ))); ++ ), ++ 'extraFieldsMessage' => 'myMessage', ++ ) ++ ) ++ ); + + $this->buildViolation('myMessage') + ->setParameter('{{ field }}', '"baz"') +@@ -153,40 +179,52 @@ + // bug fix + public function testNullNotConsideredExtraField() + { +- $data = $this->prepareTestData(array( ++ $data = $this->prepareTestData( ++ array( + 'foo' => null, +- )); ++ ) ++ ); + + $constraint = new Range(array('min' => 4)); + + $this->expectValidateValueAt(0, '[foo]', $data['foo'], array($constraint)); + +- $this->validator->validate($data, new Collection(array( +- 'fields' => array( ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'fields' => array( + 'foo' => $constraint, +- ), +- ))); ++ ), ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } + + public function testExtraFieldsAllowed() + { +- $data = $this->prepareTestData(array( ++ $data = $this->prepareTestData( ++ array( + 'foo' => 5, + 'bar' => 6, +- )); ++ ) ++ ); + + $constraint = new Range(array('min' => 4)); + + $this->expectValidateValueAt(0, '[foo]', $data['foo'], array($constraint)); + +- $this->validator->validate($data, new Collection(array( +- 'fields' => array( ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'fields' => array( + 'foo' => $constraint, +- ), +- 'allowExtraFields' => true, +- ))); ++ ), ++ 'allowExtraFields' => true, ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } +@@ -197,12 +235,16 @@ + + $constraint = new Range(array('min' => 4)); + +- $this->validator->validate($data, new Collection(array( +- 'fields' => array( ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'fields' => array( + 'foo' => $constraint, +- ), +- 'missingFieldsMessage' => 'myMessage', +- ))); ++ ), ++ 'missingFieldsMessage' => 'myMessage', ++ ) ++ ) ++ ); + + $this->buildViolation('myMessage') + ->setParameter('{{ field }}', '"foo"') +@@ -218,25 +260,35 @@ + + $constraint = new Range(array('min' => 4)); + +- $this->validator->validate($data, new Collection(array( +- 'fields' => array( ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'fields' => array( + 'foo' => $constraint, +- ), +- 'allowMissingFields' => true, +- ))); ++ ), ++ 'allowMissingFields' => true, ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } + + public function testOptionalFieldPresent() + { +- $data = $this->prepareTestData(array( ++ $data = $this->prepareTestData( ++ array( + 'foo' => null, +- )); ++ ) ++ ); + +- $this->validator->validate($data, new Collection(array( +- 'foo' => new Optional(), +- ))); ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'foo' => new Optional(), ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } +@@ -245,9 +297,13 @@ + { + $data = $this->prepareTestData(array()); + +- $this->validator->validate($data, new Collection(array( +- 'foo' => new Optional(), +- ))); ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'foo' => new Optional(), ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } +@@ -264,9 +320,13 @@ + + $data = $this->prepareTestData($array); + +- $this->validator->validate($data, new Collection(array( +- 'foo' => new Optional($constraint), +- ))); ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'foo' => new Optional($constraint), ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } +@@ -286,22 +346,32 @@ + + $data = $this->prepareTestData($array); + +- $this->validator->validate($data, new Collection(array( +- 'foo' => new Optional($constraints), +- ))); ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'foo' => new Optional($constraints), ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } + + public function testRequiredFieldPresent() + { +- $data = $this->prepareTestData(array( ++ $data = $this->prepareTestData( ++ array( + 'foo' => null, +- )); ++ ) ++ ); + +- $this->validator->validate($data, new Collection(array( +- 'foo' => new Required(), +- ))); ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'foo' => new Required(), ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } +@@ -310,12 +380,16 @@ + { + $data = $this->prepareTestData(array()); + +- $this->validator->validate($data, new Collection(array( +- 'fields' => array( ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'fields' => array( + 'foo' => new Required(), +- ), +- 'missingFieldsMessage' => 'myMessage', +- ))); ++ ), ++ 'missingFieldsMessage' => 'myMessage', ++ ) ++ ) ++ ); + + $this->buildViolation('myMessage') + ->setParameter('{{ field }}', '"foo"') +@@ -337,9 +411,13 @@ + + $data = $this->prepareTestData($array); + +- $this->validator->validate($data, new Collection(array( +- 'foo' => new Required($constraint), +- ))); ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'foo' => new Required($constraint), ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } +@@ -359,31 +437,43 @@ + + $data = $this->prepareTestData($array); + +- $this->validator->validate($data, new Collection(array( +- 'foo' => new Required($constraints), +- ))); ++ $this->validator->validate( ++ $data, new Collection( ++ array( ++ 'foo' => new Required($constraints), ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } + + public function testObjectShouldBeLeftUnchanged() + { +- $value = new \ArrayObject(array( ++ $value = new \ArrayObject( ++ array( + 'foo' => 3, +- )); ++ ) ++ ); + + $constraint = new Range(array('min' => 2)); + + $this->expectValidateValueAt(0, '[foo]', $value['foo'], array($constraint)); + +- $this->validator->validate($value, new Collection(array( +- 'fields' => array( ++ $this->validator->validate( ++ $value, new Collection( ++ array( ++ 'fields' => array( + 'foo' => $constraint, +- ), +- ))); ++ ), ++ ) ++ ) ++ ); + +- $this->assertEquals(array( ++ $this->assertEquals( ++ array( + 'foo' => 3, +- ), (array) $value); ++ ), (array) $value ++ ); + } + } + +--- vendor/symfony/validator/Tests/Constraints/ChoiceValidatorTest.php ++++ PHP_CodeSniffer +@@ -42,10 +42,12 @@ + */ + public function testExpectArrayIfMultipleIsTrue() + { +- $constraint = new Choice(array( ++ $constraint = new Choice( ++ array( + 'choices' => array('foo', 'bar'), + 'multiple' => true, +- )); ++ ) ++ ); + + $this->validator->validate('asdf', $constraint); + } +@@ -93,9 +95,11 @@ + + public function testValidChoiceCallbackClosure() + { +- $constraint = new Choice(array('callback' => function () { +- return array('foo', 'bar'); +- })); ++ $constraint = new Choice( ++ array('callback' => function () { ++ return array('foo', 'bar'); ++ }) ++ ); + + $this->validator->validate('bar', $constraint); + +@@ -125,10 +129,12 @@ + + public function testMultipleChoices() + { +- $constraint = new Choice(array( ++ $constraint = new Choice( ++ array( + 'choices' => array('foo', 'bar', 'baz'), + 'multiple' => true, +- )); ++ ) ++ ); + + $this->validator->validate(array('baz', 'bar'), $constraint); + +@@ -137,10 +143,12 @@ + + public function testInvalidChoice() + { +- $constraint = new Choice(array( ++ $constraint = new Choice( ++ array( + 'choices' => array('foo', 'bar'), + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate('baz', $constraint); + +@@ -152,12 +160,14 @@ + + public function testInvalidChoiceEmptyChoices() + { +- $constraint = new Choice(array( ++ $constraint = new Choice( ++ array( + // May happen when the choices are provided dynamically, e.g. from + // the DB or the model + 'choices' => array(), + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate('baz', $constraint); + +@@ -169,11 +179,13 @@ + + public function testInvalidChoiceMultiple() + { +- $constraint = new Choice(array( ++ $constraint = new Choice( ++ array( + 'choices' => array('foo', 'bar'), + 'multipleMessage' => 'myMessage', + 'multiple' => true, +- )); ++ ) ++ ); + + $this->validator->validate(array('foo', 'baz'), $constraint); + +@@ -186,12 +198,14 @@ + + public function testTooFewChoices() + { +- $constraint = new Choice(array( ++ $constraint = new Choice( ++ array( + 'choices' => array('foo', 'bar', 'moo', 'maa'), + 'multiple' => true, + 'min' => 2, + 'minMessage' => 'myMessage', +- )); ++ ) ++ ); + + $value = array('foo'); + +@@ -209,12 +223,14 @@ + + public function testTooManyChoices() + { +- $constraint = new Choice(array( ++ $constraint = new Choice( ++ array( + 'choices' => array('foo', 'bar', 'moo', 'maa'), + 'multiple' => true, + 'max' => 2, + 'maxMessage' => 'myMessage', +- )); ++ ) ++ ); + + $value = array('foo', 'bar', 'moo'); + +@@ -232,10 +248,12 @@ + + public function testNonStrict() + { +- $constraint = new Choice(array( ++ $constraint = new Choice( ++ array( + 'choices' => array(1, 2), + 'strict' => false, +- )); ++ ) ++ ); + + $this->validator->validate('2', $constraint); + $this->validator->validate(2, $constraint); +@@ -245,10 +263,12 @@ + + public function testStrictAllowsExactValue() + { +- $constraint = new Choice(array( ++ $constraint = new Choice( ++ array( + 'choices' => array(1, 2), + 'strict' => true, +- )); ++ ) ++ ); + + $this->validator->validate(2, $constraint); + +@@ -257,11 +277,13 @@ + + public function testStrictDisallowsDifferentType() + { +- $constraint = new Choice(array( ++ $constraint = new Choice( ++ array( + 'choices' => array(1, 2), + 'strict' => true, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate('2', $constraint); + +@@ -273,11 +295,13 @@ + + public function testNonStrictWithMultipleChoices() + { +- $constraint = new Choice(array( ++ $constraint = new Choice( ++ array( + 'choices' => array(1, 2, 3), + 'multiple' => true, + 'strict' => false, +- )); ++ ) ++ ); + + $this->validator->validate(array('2', 3), $constraint); + +@@ -286,12 +310,14 @@ + + public function testStrictWithMultipleChoices() + { +- $constraint = new Choice(array( ++ $constraint = new Choice( ++ array( + 'choices' => array(1, 2, 3), + 'multiple' => true, + 'strict' => true, + 'multipleMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate(array(2, '3'), $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/CollectionTest.php ++++ PHP_CodeSniffer +@@ -27,9 +27,11 @@ + */ + public function testRejectInvalidFieldsOption() + { +- new Collection(array( ++ new Collection( ++ array( + 'fields' => 'foo', +- )); ++ ) ++ ); + } + + /** +@@ -37,9 +39,11 @@ + */ + public function testRejectNonConstraints() + { +- new Collection(array( ++ new Collection( ++ array( + 'foo' => 'bar', +- )); ++ ) ++ ); + } + + /** +@@ -47,9 +51,11 @@ + */ + public function testRejectValidConstraint() + { +- new Collection(array( ++ new Collection( ++ array( + 'foo' => new Valid(), +- )); ++ ) ++ ); + } + + /** +@@ -57,9 +63,11 @@ + */ + public function testRejectValidConstraintWithinOptional() + { +- new Collection(array( ++ new Collection( ++ array( + 'foo' => new Optional(new Valid()), +- )); ++ ) ++ ); + } + + /** +@@ -67,45 +75,55 @@ + */ + public function testRejectValidConstraintWithinRequired() + { +- new Collection(array( ++ new Collection( ++ array( + 'foo' => new Required(new Valid()), +- )); ++ ) ++ ); + } + + public function testAcceptOptionalConstraintAsOneElementArray() + { +- $collection1 = new Collection(array( ++ $collection1 = new Collection( ++ array( + 'fields' => array( + 'alternate_email' => array( + new Optional(new Email()), + ), + ), +- )); ++ ) ++ ); + +- $collection2 = new Collection(array( ++ $collection2 = new Collection( ++ array( + 'fields' => array( + 'alternate_email' => new Optional(new Email()), + ), +- )); ++ ) ++ ); + + $this->assertEquals($collection1, $collection2); + } + + public function testAcceptRequiredConstraintAsOneElementArray() + { +- $collection1 = new Collection(array( ++ $collection1 = new Collection( ++ array( + 'fields' => array( + 'alternate_email' => array( + new Required(new Email()), + ), + ), +- )); ++ ) ++ ); + +- $collection2 = new Collection(array( ++ $collection2 = new Collection( ++ array( + 'fields' => array( + 'alternate_email' => new Required(new Email()), + ), +- )); ++ ) ++ ); + + $this->assertEquals($collection1, $collection2); + } + +--- vendor/symfony/validator/Tests/Constraints/CardSchemeValidatorTest.php ++++ PHP_CodeSniffer +@@ -56,10 +56,12 @@ + */ + public function testInvalidNumbers($scheme, $number, $code) + { +- $constraint = new CardScheme(array( ++ $constraint = new CardScheme( ++ array( + 'schemes' => $scheme, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($number, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/IsNullValidatorTest.php ++++ PHP_CodeSniffer +@@ -39,9 +39,11 @@ + */ + public function testInvalidValues($value, $valueAsString) + { +- $constraint = new IsNull(array( ++ $constraint = new IsNull( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/LuhnValidatorTest.php ++++ PHP_CodeSniffer +@@ -80,9 +80,11 @@ + */ + public function testInvalidNumbers($number, $code) + { +- $constraint = new Luhn(array( ++ $constraint = new Luhn( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($number, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/IbanValidatorTest.php ++++ PHP_CodeSniffer +@@ -424,9 +424,11 @@ + + private function assertViolationRaised($iban, $code) + { +- $constraint = new Iban(array( ++ $constraint = new Iban( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($iban, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/IsbnValidatorTest.php ++++ PHP_CodeSniffer +@@ -156,9 +156,11 @@ + */ + public function testValidIsbn10($isbn) + { +- $constraint = new Isbn(array( ++ $constraint = new Isbn( ++ array( + 'type' => 'isbn10', +- )); ++ ) ++ ); + + $this->validator->validate($isbn, $constraint); + +@@ -170,10 +172,12 @@ + */ + public function testInvalidIsbn10($isbn, $code) + { +- $constraint = new Isbn(array( ++ $constraint = new Isbn( ++ array( + 'type' => 'isbn10', + 'isbn10Message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($isbn, $constraint); + +@@ -200,10 +204,12 @@ + */ + public function testInvalidIsbn13($isbn, $code) + { +- $constraint = new Isbn(array( ++ $constraint = new Isbn( ++ array( + 'type' => 'isbn13', + 'isbn13Message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($isbn, $constraint); + +@@ -230,9 +236,11 @@ + */ + public function testInvalidIsbnAnyIsbn10($isbn, $code) + { +- $constraint = new Isbn(array( ++ $constraint = new Isbn( ++ array( + 'bothIsbnMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($isbn, $constraint); + +@@ -252,9 +260,11 @@ + */ + public function testInvalidIsbnAnyIsbn13($isbn, $code) + { +- $constraint = new Isbn(array( ++ $constraint = new Isbn( ++ array( + 'bothIsbnMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($isbn, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/CallbackValidatorTest.php ++++ PHP_CodeSniffer +@@ -102,11 +102,13 @@ + public function testClosure() + { + $object = new CallbackValidatorTest_Object(); +- $constraint = new Callback(function ($object, ExecutionContextInterface $context) { +- $context->addViolation('My message', array('{{ value }}' => 'foobar')); ++ $constraint = new Callback( ++ function ($object, ExecutionContextInterface $context) { ++ $context->addViolation('My message', array('{{ value }}' => 'foobar')); + +- return false; +- }); ++ return false; ++ } ++ ); + + $this->validator->validate($object, $constraint); + +@@ -117,11 +119,13 @@ + + public function testClosureNullObject() + { +- $constraint = new Callback(function ($object, ExecutionContextInterface $context) { +- $context->addViolation('My message', array('{{ value }}' => 'foobar')); ++ $constraint = new Callback( ++ function ($object, ExecutionContextInterface $context) { ++ $context->addViolation('My message', array('{{ value }}' => 'foobar')); + +- return false; +- }); ++ return false; ++ } ++ ); + + $this->validator->validate(null, $constraint); + +@@ -133,13 +137,15 @@ + public function testClosureExplicitName() + { + $object = new CallbackValidatorTest_Object(); +- $constraint = new Callback(array( ++ $constraint = new Callback( ++ array( + 'callback' => function ($object, ExecutionContextInterface $context) { + $context->addViolation('My message', array('{{ value }}' => 'foobar')); + + return false; + }, +- )); ++ ) ++ ); + + $this->validator->validate($object, $constraint); + +@@ -174,9 +180,11 @@ + public function testArrayCallableExplicitName() + { + $object = new CallbackValidatorTest_Object(); +- $constraint = new Callback(array( ++ $constraint = new Callback( ++ array( + 'callback' => array(__CLASS__.'_Class', 'validateCallback'), +- )); ++ ) ++ ); + + $this->validator->validate($object, $constraint); + +@@ -242,9 +250,11 @@ + public function testLegacyMultipleMethodsBcExplicitName() + { + $object = new CallbackValidatorTest_Object(); +- $constraint = new Callback(array( ++ $constraint = new Callback( ++ array( + 'methods' => array('validate', 'validateStatic'), +- )); ++ ) ++ ); + + $this->validator->validate($object, $constraint); + +@@ -262,9 +272,11 @@ + public function testLegacySingleStaticMethodBc() + { + $object = new CallbackValidatorTest_Object(); +- $constraint = new Callback(array( ++ $constraint = new Callback( ++ array( + array(__CLASS__.'_Class', 'validateCallback'), +- )); ++ ) ++ ); + + $this->validator->validate($object, $constraint); + +@@ -280,9 +292,11 @@ + public function testLegacySingleStaticMethodBcExplicitName() + { + $object = new CallbackValidatorTest_Object(); +- $constraint = new Callback(array( ++ $constraint = new Callback( ++ array( + 'methods' => array(array(__CLASS__.'_Class', 'validateCallback')), +- )); ++ ) ++ ); + + $this->validator->validate($object, $constraint); + +@@ -319,10 +333,14 @@ + { + $object = new CallbackValidatorTest_Object(); + +- $this->validator->validate($object, new Callback(array( +- 'callback' => 'validate', +- 'methods' => array('validateStatic'), +- ))); ++ $this->validator->validate( ++ $object, new Callback( ++ array( ++ 'callback' => 'validate', ++ 'methods' => array('validateStatic'), ++ ) ++ ) ++ ); + } + + public function testConstraintGetTargets() + +--- vendor/symfony/validator/Tests/Constraints/RangeValidatorTest.php ++++ PHP_CodeSniffer +@@ -107,10 +107,12 @@ + */ + public function testInvalidValuesMin($value, $formattedValue) + { +- $constraint = new Range(array( ++ $constraint = new Range( ++ array( + 'min' => 10, + 'minMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -126,10 +128,12 @@ + */ + public function testInvalidValuesMax($value, $formattedValue) + { +- $constraint = new Range(array( ++ $constraint = new Range( ++ array( + 'max' => 20, + 'maxMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -145,12 +149,14 @@ + */ + public function testInvalidValuesCombinedMax($value, $formattedValue) + { +- $constraint = new Range(array( ++ $constraint = new Range( ++ array( + 'min' => 10, + 'max' => 20, + 'minMessage' => 'myMinMessage', + 'maxMessage' => 'myMaxMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -166,12 +172,14 @@ + */ + public function testInvalidValuesCombinedMin($value, $formattedValue) + { +- $constraint = new Range(array( ++ $constraint = new Range( ++ array( + 'min' => 10, + 'max' => 20, + 'minMessage' => 'myMinMessage', + 'maxMessage' => 'myMaxMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -289,10 +297,12 @@ + // Make sure we have the correct version loaded + IntlTestHelper::requireIntl($this); + +- $constraint = new Range(array( ++ $constraint = new Range( ++ array( + 'min' => 'March 10, 2014', + 'minMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -312,10 +322,12 @@ + // Make sure we have the correct version loaded + IntlTestHelper::requireIntl($this); + +- $constraint = new Range(array( ++ $constraint = new Range( ++ array( + 'max' => 'March 20, 2014', + 'maxMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -335,12 +347,14 @@ + // Make sure we have the correct version loaded + IntlTestHelper::requireIntl($this); + +- $constraint = new Range(array( ++ $constraint = new Range( ++ array( + 'min' => 'March 10, 2014', + 'max' => 'March 20, 2014', + 'minMessage' => 'myMinMessage', + 'maxMessage' => 'myMaxMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -360,12 +374,14 @@ + // Make sure we have the correct version loaded + IntlTestHelper::requireIntl($this); + +- $constraint = new Range(array( ++ $constraint = new Range( ++ array( + 'min' => 'March 10, 2014', + 'max' => 'March 20, 2014', + 'minMessage' => 'myMinMessage', + 'maxMessage' => 'myMaxMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -389,11 +405,15 @@ + + public function testNonNumeric() + { +- $this->validator->validate('abcd', new Range(array( +- 'min' => 10, +- 'max' => 20, +- 'invalidMessage' => 'myMessage', +- ))); ++ $this->validator->validate( ++ 'abcd', new Range( ++ array( ++ 'min' => 10, ++ 'max' => 20, ++ 'invalidMessage' => 'myMessage', ++ ) ++ ) ++ ); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"abcd"') + +--- vendor/symfony/validator/Tests/Constraints/ExpressionValidatorTest.php ++++ PHP_CodeSniffer +@@ -31,10 +31,12 @@ + + public function testExpressionIsEvaluatedWithNullValue() + { +- $constraint = new Expression(array( ++ $constraint = new Expression( ++ array( + 'expression' => 'false', + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate(null, $constraint); + +@@ -46,10 +48,12 @@ + + public function testExpressionIsEvaluatedWithEmptyStringValue() + { +- $constraint = new Expression(array( ++ $constraint = new Expression( ++ array( + 'expression' => 'false', + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate('', $constraint); + +@@ -75,10 +79,12 @@ + + public function testFailingExpressionAtObjectLevel() + { +- $constraint = new Expression(array( ++ $constraint = new Expression( ++ array( + 'expression' => 'this.data == 1', + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $object = new Entity(); + $object->data = '2'; +@@ -111,10 +117,12 @@ + + public function testFailingExpressionAtPropertyLevel() + { +- $constraint = new Expression(array( ++ $constraint = new Expression( ++ array( + 'expression' => 'value == this.data', + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $object = new Entity(); + $object->data = '1'; +@@ -153,10 +161,12 @@ + + public function testFailingExpressionAtNestedPropertyLevel() + { +- $constraint = new Expression(array( ++ $constraint = new Expression( ++ array( + 'expression' => 'value == this.data', + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $object = new Entity(); + $object->data = '1'; +@@ -200,10 +210,12 @@ + */ + public function testFailingExpressionAtPropertyLevelWithoutRoot() + { +- $constraint = new Expression(array( ++ $constraint = new Expression( ++ array( + 'expression' => 'value == "1"', + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->setRoot('2'); + $this->setPropertyPath(''); +@@ -220,20 +232,26 @@ + + public function testExpressionLanguageUsage() + { +- $constraint = new Expression(array( ++ $constraint = new Expression( ++ array( + 'expression' => 'false', +- )); ++ ) ++ ); + + $expressionLanguage = $this->getMock('Symfony\Component\ExpressionLanguage\ExpressionLanguage'); + + $used = false; + + $expressionLanguage->method('evaluate') +- ->will($this->returnCallback(function () use (&$used) { +- $used = true; +- +- return true; +- })); ++ ->will( ++ $this->returnCallback( ++ function () use (&$used) { ++ $used = true; ++ ++ return true; ++ } ++ ) ++ ); + + $validator = new ExpressionValidator(null, $expressionLanguage); + $validator->initialize($this->createContext()); + +--- vendor/symfony/validator/Tests/Constraints/NotBlankValidatorTest.php ++++ PHP_CodeSniffer +@@ -50,9 +50,11 @@ + + public function testNullIsInvalid() + { +- $constraint = new NotBlank(array( ++ $constraint = new NotBlank( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate(null, $constraint); + +@@ -64,9 +66,11 @@ + + public function testBlankIsInvalid() + { +- $constraint = new NotBlank(array( ++ $constraint = new NotBlank( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate('', $constraint); + +@@ -78,9 +82,11 @@ + + public function testFalseIsInvalid() + { +- $constraint = new NotBlank(array( ++ $constraint = new NotBlank( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate(false, $constraint); + +@@ -92,9 +98,11 @@ + + public function testEmptyArrayIsInvalid() + { +- $constraint = new NotBlank(array( ++ $constraint = new NotBlank( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate(array(), $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/CountValidatorTest.php ++++ PHP_CodeSniffer +@@ -112,10 +112,12 @@ + */ + public function testTooManyValues($value) + { +- $constraint = new Count(array( ++ $constraint = new Count( ++ array( + 'max' => 4, + 'maxMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -133,10 +135,12 @@ + */ + public function testTooFewValues($value) + { +- $constraint = new Count(array( ++ $constraint = new Count( ++ array( + 'min' => 4, + 'minMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -154,11 +158,13 @@ + */ + public function testTooManyValuesExact($value) + { +- $constraint = new Count(array( ++ $constraint = new Count( ++ array( + 'min' => 4, + 'max' => 4, + 'exactMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + +@@ -176,11 +182,13 @@ + */ + public function testTooFewValuesExact($value) + { +- $constraint = new Count(array( ++ $constraint = new Count( ++ array( + 'min' => 4, + 'max' => 4, + 'exactMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/EmailValidatorTest.php ++++ PHP_CodeSniffer +@@ -77,9 +77,11 @@ + */ + public function testInvalidEmails($email) + { +- $constraint = new Email(array( ++ $constraint = new Email( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($email, $constraint); + +@@ -116,10 +118,12 @@ + { + DnsMock::withMockedHosts(array('example.com' => array(array('type' => $violation ? false : $type)))); + +- $constraint = new Email(array( ++ $constraint = new Email( ++ array( + 'message' => 'myMessage', + 'MX' === $type ? 'checkMX' : 'checkHost' => true, +- )); ++ ) ++ ); + + $this->validator->validate('foo@example.com', $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/CurrencyValidatorTest.php ++++ PHP_CodeSniffer +@@ -90,9 +90,11 @@ + */ + public function testInvalidCurrencies($currency) + { +- $constraint = new Currency(array( ++ $constraint = new Currency( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($currency, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/TypeValidatorTest.php ++++ PHP_CodeSniffer +@@ -49,10 +49,12 @@ + + public function testEmptyIsInvalidIfNoString() + { +- $constraint = new Type(array( ++ $constraint = new Type( ++ array( + 'type' => 'integer', + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate('', $constraint); + +@@ -117,10 +119,12 @@ + */ + public function testInvalidValues($value, $type, $valueAsString) + { +- $constraint = new Type(array( ++ $constraint = new Type( ++ array( + 'type' => $type, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/DateValidatorTest.php ++++ PHP_CodeSniffer +@@ -80,9 +80,11 @@ + */ + public function testInvalidDates($date, $code) + { +- $constraint = new Date(array( ++ $constraint = new Date( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($date, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/BicValidatorTest.php ++++ PHP_CodeSniffer +@@ -63,9 +63,11 @@ + */ + public function testInvalidBics($bic, $code) + { +- $constraint = new Bic(array( ++ $constraint = new Bic( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($bic, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/FileValidatorTest.php ++++ PHP_CodeSniffer +@@ -169,10 +169,12 @@ + fwrite($this->file, '0'); + fclose($this->file); + +- $constraint = new File(array( ++ $constraint = new File( ++ array( + 'maxSize' => $limit, + 'maxSizeMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($this->getFile($this->path), $constraint); + +@@ -217,10 +219,12 @@ + fwrite($this->file, '0'); + fclose($this->file); + +- $constraint = new File(array( ++ $constraint = new File( ++ array( + 'maxSize' => $limit, + 'maxSizeMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($this->getFile($this->path), $constraint); + +@@ -232,9 +236,11 @@ + */ + public function testInvalidMaxSize() + { +- $constraint = new File(array( ++ $constraint = new File( ++ array( + 'maxSize' => '1abc', +- )); ++ ) ++ ); + + $this->validator->validate($this->path, $constraint); + } +@@ -270,11 +276,13 @@ + fwrite($this->file, '0'); + fclose($this->file); + +- $constraint = new File(array( ++ $constraint = new File( ++ array( + 'maxSize' => $limit, + 'binaryFormat' => $binaryFormat, + 'maxSizeMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($this->getFile($this->path), $constraint); + +@@ -302,9 +310,11 @@ + ->method('getMimeType') + ->will($this->returnValue('image/jpg')); + +- $constraint = new File(array( ++ $constraint = new File( ++ array( + 'mimeTypes' => array('image/png', 'image/jpg'), +- )); ++ ) ++ ); + + $this->validator->validate($file, $constraint); + +@@ -326,9 +336,11 @@ + ->method('getMimeType') + ->will($this->returnValue('image/jpg')); + +- $constraint = new File(array( ++ $constraint = new File( ++ array( + 'mimeTypes' => array('image/*'), +- )); ++ ) ++ ); + + $this->validator->validate($file, $constraint); + +@@ -350,10 +362,12 @@ + ->method('getMimeType') + ->will($this->returnValue('application/pdf')); + +- $constraint = new File(array( ++ $constraint = new File( ++ array( + 'mimeTypes' => array('image/png', 'image/jpg'), + 'mimeTypesMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($file, $constraint); + +@@ -380,10 +394,12 @@ + ->method('getMimeType') + ->will($this->returnValue('application/pdf')); + +- $constraint = new File(array( ++ $constraint = new File( ++ array( + 'mimeTypes' => array('image/*', 'image/jpg'), + 'mimeTypesMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($file, $constraint); + +@@ -399,9 +415,11 @@ + { + ftruncate($this->file, 0); + +- $constraint = new File(array( ++ $constraint = new File( ++ array( + 'disallowEmptyMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($this->getFile($this->path), $constraint); + +@@ -418,10 +436,12 @@ + { + $file = new UploadedFile('/path/to/file', 'originalName', 'mime', 0, $error); + +- $constraint = new File(array( ++ $constraint = new File( ++ array( + $message => 'myMessage', + 'maxSize' => $maxSize, +- )); ++ ) ++ ); + + $this->validator->validate($file, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/BlankValidatorTest.php ++++ PHP_CodeSniffer +@@ -46,9 +46,11 @@ + */ + public function testInvalidValues($value, $valueAsString) + { +- $constraint = new Blank(array( ++ $constraint = new Blank( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/UrlValidatorTest.php ++++ PHP_CodeSniffer +@@ -135,9 +135,11 @@ + */ + public function testInvalidUrls($url) + { +- $constraint = new Url(array( ++ $constraint = new Url( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($url, $constraint); + +@@ -175,9 +177,11 @@ + */ + public function testCustomProtocolIsValid($url) + { +- $constraint = new Url(array( ++ $constraint = new Url( ++ array( + 'protocols' => array('ftp', 'file', 'git'), +- )); ++ ) ++ ); + + $this->validator->validate($url, $constraint); + +@@ -201,10 +205,12 @@ + { + DnsMock::withMockedHosts(array('example.com' => array(array('type' => $violation ? '' : 'A')))); + +- $constraint = new Url(array( ++ $constraint = new Url( ++ array( + 'checkDNS' => true, + 'dnsMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate('http://example.com', $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/RegexValidatorTest.php ++++ PHP_CodeSniffer +@@ -75,10 +75,12 @@ + */ + public function testInvalidValues($value) + { +- $constraint = new Regex(array( ++ $constraint = new Regex( ++ array( + 'pattern' => '/^[0-9]+$/', + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($value, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/CountryValidatorTest.php ++++ PHP_CodeSniffer +@@ -74,9 +74,11 @@ + */ + public function testInvalidCountries($country) + { +- $constraint = new Country(array( ++ $constraint = new Country( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($country, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/AllTest.php ++++ PHP_CodeSniffer +@@ -24,9 +24,11 @@ + */ + public function testRejectNonConstraints() + { +- new All(array( ++ new All( ++ array( + 'foo', +- )); ++ ) ++ ); + } + + /** +@@ -34,8 +36,10 @@ + */ + public function testRejectValidConstraint() + { +- new All(array( ++ new All( ++ array( + new Valid(), +- )); ++ ) ++ ); + } + } + +--- vendor/symfony/validator/Tests/Constraints/UuidValidatorTest.php ++++ PHP_CodeSniffer +@@ -85,9 +85,11 @@ + */ + public function testInvalidStrictUuids($uuid, $code, $versions = null) + { +- $constraint = new Uuid(array( ++ $constraint = new Uuid( ++ array( + 'message' => 'testMessage', +- )); ++ ) ++ ); + + if (null !== $versions) { + $constraint->versions = $versions; +@@ -152,9 +154,11 @@ + */ + public function testValidNonStrictUuids($uuid) + { +- $constraint = new Uuid(array( ++ $constraint = new Uuid( ++ array( + 'strict' => false, +- )); ++ ) ++ ); + + $this->validator->validate($uuid, $constraint); + +@@ -183,10 +187,12 @@ + */ + public function testInvalidNonStrictUuids($uuid, $code) + { +- $constraint = new Uuid(array( ++ $constraint = new Uuid( ++ array( + 'strict' => false, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($uuid, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/LocaleValidatorTest.php ++++ PHP_CodeSniffer +@@ -76,9 +76,11 @@ + */ + public function testInvalidLocales($locale) + { +- $constraint = new Locale(array( ++ $constraint = new Locale( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($locale, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/RegexTest.php ++++ PHP_CodeSniffer +@@ -65,10 +65,12 @@ + */ + public function testGetHtmlPattern($pattern, $htmlPattern, $match = true) + { +- $constraint = new Regex(array( ++ $constraint = new Regex( ++ array( + 'pattern' => $pattern, + 'match' => $match, +- )); ++ ) ++ ); + + $this->assertSame($pattern, $constraint->pattern); + $this->assertSame($htmlPattern, $constraint->getHtmlPattern()); +@@ -76,10 +78,12 @@ + + public function testGetCustomHtmlPattern() + { +- $constraint = new Regex(array( ++ $constraint = new Regex( ++ array( + 'pattern' => '((?![0-9]$|[a-z]+).)*', + 'htmlPattern' => 'foobar', +- )); ++ ) ++ ); + + $this->assertSame('((?![0-9]$|[a-z]+).)*', $constraint->pattern); + $this->assertSame('foobar', $constraint->getHtmlPattern()); + +--- vendor/symfony/validator/Tests/Constraints/IpValidatorTest.php ++++ PHP_CodeSniffer +@@ -54,9 +54,11 @@ + */ + public function testInvalidValidatorVersion() + { +- new Ip(array( ++ new Ip( ++ array( + 'version' => 666, +- )); ++ ) ++ ); + } + + /** +@@ -64,9 +66,13 @@ + */ + public function testValidIpsV4($ip) + { +- $this->validator->validate($ip, new Ip(array( +- 'version' => Ip::V4, +- ))); ++ $this->validator->validate( ++ $ip, new Ip( ++ array( ++ 'version' => Ip::V4, ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } +@@ -90,9 +96,13 @@ + */ + public function testValidIpsV6($ip) + { +- $this->validator->validate($ip, new Ip(array( +- 'version' => Ip::V6, +- ))); ++ $this->validator->validate( ++ $ip, new Ip( ++ array( ++ 'version' => Ip::V6, ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } +@@ -127,9 +137,13 @@ + */ + public function testValidIpsAll($ip) + { +- $this->validator->validate($ip, new Ip(array( +- 'version' => Ip::ALL, +- ))); ++ $this->validator->validate( ++ $ip, new Ip( ++ array( ++ 'version' => Ip::ALL, ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } +@@ -144,10 +158,12 @@ + */ + public function testInvalidIpsV4($ip) + { +- $constraint = new Ip(array( ++ $constraint = new Ip( ++ array( + 'version' => Ip::V4, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($ip, $constraint); + +@@ -177,10 +193,12 @@ + */ + public function testInvalidPrivateIpsV4($ip) + { +- $constraint = new Ip(array( ++ $constraint = new Ip( ++ array( + 'version' => Ip::V4_NO_PRIV, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($ip, $constraint); + +@@ -204,10 +222,12 @@ + */ + public function testInvalidReservedIpsV4($ip) + { +- $constraint = new Ip(array( ++ $constraint = new Ip( ++ array( + 'version' => Ip::V4_NO_RES, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($ip, $constraint); + +@@ -231,10 +251,12 @@ + */ + public function testInvalidPublicIpsV4($ip) + { +- $constraint = new Ip(array( ++ $constraint = new Ip( ++ array( + 'version' => Ip::V4_ONLY_PUBLIC, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($ip, $constraint); + +@@ -254,10 +276,12 @@ + */ + public function testInvalidIpsV6($ip) + { +- $constraint = new Ip(array( ++ $constraint = new Ip( ++ array( + 'version' => Ip::V6, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($ip, $constraint); + +@@ -291,10 +315,12 @@ + */ + public function testInvalidPrivateIpsV6($ip) + { +- $constraint = new Ip(array( ++ $constraint = new Ip( ++ array( + 'version' => Ip::V6_NO_PRIV, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($ip, $constraint); + +@@ -318,10 +344,12 @@ + */ + public function testInvalidReservedIpsV6($ip) + { +- $constraint = new Ip(array( ++ $constraint = new Ip( ++ array( + 'version' => Ip::V6_NO_RES, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($ip, $constraint); + +@@ -344,10 +372,12 @@ + */ + public function testInvalidPublicIpsV6($ip) + { +- $constraint = new Ip(array( ++ $constraint = new Ip( ++ array( + 'version' => Ip::V6_ONLY_PUBLIC, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($ip, $constraint); + +@@ -367,10 +397,12 @@ + */ + public function testInvalidIpsAll($ip) + { +- $constraint = new Ip(array( ++ $constraint = new Ip( ++ array( + 'version' => Ip::ALL, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($ip, $constraint); + +@@ -390,10 +422,12 @@ + */ + public function testInvalidPrivateIpsAll($ip) + { +- $constraint = new Ip(array( ++ $constraint = new Ip( ++ array( + 'version' => Ip::ALL_NO_PRIV, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($ip, $constraint); + +@@ -413,10 +447,12 @@ + */ + public function testInvalidReservedIpsAll($ip) + { +- $constraint = new Ip(array( ++ $constraint = new Ip( ++ array( + 'version' => Ip::ALL_NO_RES, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($ip, $constraint); + +@@ -436,10 +472,12 @@ + */ + public function testInvalidPublicIpsAll($ip) + { +- $constraint = new Ip(array( ++ $constraint = new Ip( ++ array( + 'version' => Ip::ALL_ONLY_PUBLIC, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($ip, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/IssnValidatorTest.php ++++ PHP_CodeSniffer +@@ -125,10 +125,12 @@ + */ + public function testCaseSensitiveIssns($issn) + { +- $constraint = new Issn(array( ++ $constraint = new Issn( ++ array( + 'caseSensitive' => true, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($issn, $constraint); + +@@ -143,10 +145,12 @@ + */ + public function testRequireHyphenIssns($issn) + { +- $constraint = new Issn(array( ++ $constraint = new Issn( ++ array( + 'requireHyphen' => true, + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($issn, $constraint); + +@@ -173,9 +177,11 @@ + */ + public function testInvalidIssn($issn, $code) + { +- $constraint = new Issn(array( ++ $constraint = new Issn( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($issn, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/IsTrueValidatorTest.php ++++ PHP_CodeSniffer +@@ -43,9 +43,11 @@ + + public function testFalseIsInvalid() + { +- $constraint = new IsTrue(array( ++ $constraint = new IsTrue( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate(false, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/LanguageValidatorTest.php ++++ PHP_CodeSniffer +@@ -74,9 +74,11 @@ + */ + public function testInvalidLanguages($language) + { +- $constraint = new Language(array( ++ $constraint = new Language( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate($language, $constraint); + +@@ -101,9 +103,13 @@ + \Locale::setDefault('fr_FR'); + $existingLanguage = 'en'; + +- $this->validator->validate($existingLanguage, new Language(array( +- 'message' => 'aMessage', +- ))); ++ $this->validator->validate( ++ $existingLanguage, new Language( ++ array( ++ 'message' => 'aMessage', ++ ) ++ ) ++ ); + + $this->assertNoViolation(); + } + +--- vendor/symfony/validator/Tests/Constraints/IsFalseValidatorTest.php ++++ PHP_CodeSniffer +@@ -43,9 +43,11 @@ + + public function testTrueIsInvalid() + { +- $constraint = new IsFalse(array( ++ $constraint = new IsFalse( ++ array( + 'message' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate(true, $constraint); + + +--- vendor/symfony/validator/Tests/Constraints/FileValidatorPathTest.php ++++ PHP_CodeSniffer +@@ -22,9 +22,11 @@ + + public function testFileNotFound() + { +- $constraint = new File(array( ++ $constraint = new File( ++ array( + 'notFoundMessage' => 'myMessage', +- )); ++ ) ++ ); + + $this->validator->validate('foobar', $constraint); + + +--- vendor/symfony/validator/Tests/ValidatorBuilderTest.php ++++ PHP_CodeSniffer +@@ -33,9 +33,11 @@ + + public function testAddObjectInitializer() + { +- $this->assertSame($this->builder, $this->builder->addObjectInitializer( +- $this->getMock('Symfony\Component\Validator\ObjectInitializerInterface') +- )); ++ $this->assertSame( ++ $this->builder, $this->builder->addObjectInitializer( ++ $this->getMock('Symfony\Component\Validator\ObjectInitializerInterface') ++ ) ++ ); + } + + public function testAddObjectInitializers() +@@ -85,22 +87,28 @@ + + public function testSetMetadataCache() + { +- $this->assertSame($this->builder, $this->builder->setMetadataCache( +- $this->getMock('Symfony\Component\Validator\Mapping\Cache\CacheInterface')) ++ $this->assertSame( ++ $this->builder, $this->builder->setMetadataCache( ++ $this->getMock('Symfony\Component\Validator\Mapping\Cache\CacheInterface') ++ ) + ); + } + + public function testSetConstraintValidatorFactory() + { +- $this->assertSame($this->builder, $this->builder->setConstraintValidatorFactory( +- $this->getMock('Symfony\Component\Validator\ConstraintValidatorFactoryInterface')) ++ $this->assertSame( ++ $this->builder, $this->builder->setConstraintValidatorFactory( ++ $this->getMock('Symfony\Component\Validator\ConstraintValidatorFactoryInterface') ++ ) + ); + } + + public function testSetTranslator() + { +- $this->assertSame($this->builder, $this->builder->setTranslator( +- $this->getMock('Symfony\Component\Translation\TranslatorInterface')) ++ $this->assertSame( ++ $this->builder, $this->builder->setTranslator( ++ $this->getMock('Symfony\Component\Translation\TranslatorInterface') ++ ) + ); + } + + +--- vendor/symfony/validator/Tests/LegacyExecutionContextTest.php ++++ PHP_CodeSniffer +@@ -98,16 +98,20 @@ + + $this->context->addViolation('Error', array('foo' => 'bar'), 'invalid'); + +- $this->assertEquals(new ConstraintViolationList(array( +- new ConstraintViolation( +- 'Translated error', +- 'Error', +- array('foo' => 'bar'), +- 'Root', +- 'foo.bar', +- 'invalid' +- ), +- )), $this->context->getViolations()); ++ $this->assertEquals( ++ new ConstraintViolationList( ++ array( ++ new ConstraintViolation( ++ 'Translated error', ++ 'Error', ++ array('foo' => 'bar'), ++ 'Root', ++ 'foo.bar', ++ 'invalid' ++ ), ++ ) ++ ), $this->context->getViolations() ++ ); + } + + public function testAddViolationUsesPreconfiguredValueIfNotPassed() +@@ -119,16 +123,20 @@ + + $this->context->addViolation('Error'); + +- $this->assertEquals(new ConstraintViolationList(array( +- new ConstraintViolation( +- 'Translated error', +- 'Error', +- array(), +- 'Root', +- 'foo.bar', +- 'currentValue' +- ), +- )), $this->context->getViolations()); ++ $this->assertEquals( ++ new ConstraintViolationList( ++ array( ++ new ConstraintViolation( ++ 'Translated error', ++ 'Error', ++ array(), ++ 'Root', ++ 'foo.bar', ++ 'currentValue' ++ ), ++ ) ++ ), $this->context->getViolations() ++ ); + } + + public function testAddViolationUsesPassedNullValue() +@@ -146,25 +154,29 @@ + $this->context->addViolation('Error', array('foo1' => 'bar1'), null); + $this->context->addViolation('Choice error', array('foo2' => 'bar2'), null, 1); + +- $this->assertEquals(new ConstraintViolationList(array( +- new ConstraintViolation( +- 'Translated error', +- 'Error', +- array('foo1' => 'bar1'), +- 'Root', +- 'foo.bar', +- null +- ), +- new ConstraintViolation( +- 'Translated choice error', +- 'Choice error', +- array('foo2' => 'bar2'), +- 'Root', +- 'foo.bar', +- null, +- 1 +- ), +- )), $this->context->getViolations()); ++ $this->assertEquals( ++ new ConstraintViolationList( ++ array( ++ new ConstraintViolation( ++ 'Translated error', ++ 'Error', ++ array('foo1' => 'bar1'), ++ 'Root', ++ 'foo.bar', ++ null ++ ), ++ new ConstraintViolation( ++ 'Translated choice error', ++ 'Choice error', ++ array('foo2' => 'bar2'), ++ 'Root', ++ 'foo.bar', ++ null, ++ 1 ++ ), ++ ) ++ ), $this->context->getViolations() ++ ); + } + + public function testAddViolationAt() +@@ -177,16 +189,20 @@ + // override preconfigured property path + $this->context->addViolationAt('bam.baz', 'Error', array('foo' => 'bar'), 'invalid'); + +- $this->assertEquals(new ConstraintViolationList(array( +- new ConstraintViolation( +- 'Translated error', +- 'Error', +- array('foo' => 'bar'), +- 'Root', +- 'foo.bar.bam.baz', +- 'invalid' +- ), +- )), $this->context->getViolations()); ++ $this->assertEquals( ++ new ConstraintViolationList( ++ array( ++ new ConstraintViolation( ++ 'Translated error', ++ 'Error', ++ array('foo' => 'bar'), ++ 'Root', ++ 'foo.bar.bam.baz', ++ 'invalid' ++ ), ++ ) ++ ), $this->context->getViolations() ++ ); + } + + public function testAddViolationAtUsesPreconfiguredValueIfNotPassed() +@@ -198,16 +214,20 @@ + + $this->context->addViolationAt('bam.baz', 'Error'); + +- $this->assertEquals(new ConstraintViolationList(array( +- new ConstraintViolation( +- 'Translated error', +- 'Error', +- array(), +- 'Root', +- 'foo.bar.bam.baz', +- 'currentValue' +- ), +- )), $this->context->getViolations()); ++ $this->assertEquals( ++ new ConstraintViolationList( ++ array( ++ new ConstraintViolation( ++ 'Translated error', ++ 'Error', ++ array(), ++ 'Root', ++ 'foo.bar.bam.baz', ++ 'currentValue' ++ ), ++ ) ++ ), $this->context->getViolations() ++ ); + } + + public function testAddViolationAtUsesPassedNullValue() +@@ -225,25 +245,29 @@ + $this->context->addViolationAt('bam.baz', 'Error', array('foo' => 'bar'), null); + $this->context->addViolationAt('bam.baz', 'Choice error', array('foo' => 'bar'), null, 2); + +- $this->assertEquals(new ConstraintViolationList(array( +- new ConstraintViolation( +- 'Translated error', +- 'Error', +- array('foo' => 'bar'), +- 'Root', +- 'foo.bar.bam.baz', +- null +- ), +- new ConstraintViolation( +- 'Translated choice error', +- 'Choice error', +- array('foo' => 'bar'), +- 'Root', +- 'foo.bar.bam.baz', +- null, +- 2 +- ), +- )), $this->context->getViolations()); ++ $this->assertEquals( ++ new ConstraintViolationList( ++ array( ++ new ConstraintViolation( ++ 'Translated error', ++ 'Error', ++ array('foo' => 'bar'), ++ 'Root', ++ 'foo.bar.bam.baz', ++ null ++ ), ++ new ConstraintViolation( ++ 'Translated choice error', ++ 'Choice error', ++ array('foo' => 'bar'), ++ 'Root', ++ 'foo.bar.bam.baz', ++ null, ++ 2 ++ ), ++ ) ++ ), $this->context->getViolations() ++ ); + } + + public function testAddViolationPluralTranslationError() +@@ -283,17 +307,25 @@ + + public function testGetPropertyPathWithNestedCollectionsAndAllMixed() + { +- $constraints = new Collection(array( +- 'shelves' => new All(array('constraints' => array( +- new Collection(array( ++ $constraints = new Collection( ++ array( ++ 'shelves' => new All( ++ array('constraints' => array( ++ new Collection( ++ array( + 'name' => new ConstraintA(), +- 'books' => new All(array('constraints' => array( ++ 'books' => new All( ++ array('constraints' => array( + new ConstraintA(), +- ))), +- )), +- ))), ++ )) ++ ), ++ ) ++ ), ++ )) ++ ), + 'name' => new ConstraintA(), +- )); ++ ) ++ ); + $data = array( + 'shelves' => array( + array( + +--- vendor/symfony/validator/Tests/Mapping/Loader/AnnotationLoaderTest.php ++++ PHP_CodeSniffer +@@ -59,14 +59,22 @@ + $expected->addPropertyConstraint('firstName', new Range(array('min' => 3))); + $expected->addPropertyConstraint('firstName', new All(array(new NotNull(), new Range(array('min' => 3))))); + $expected->addPropertyConstraint('firstName', new All(array('constraints' => array(new NotNull(), new Range(array('min' => 3)))))); +- $expected->addPropertyConstraint('firstName', new Collection(array('fields' => array( +- 'foo' => array(new NotNull(), new Range(array('min' => 3))), +- 'bar' => new Range(array('min' => 5)), +- )))); +- $expected->addPropertyConstraint('firstName', new Choice(array( +- 'message' => 'Must be one of %choices%', +- 'choices' => array('A', 'B'), +- ))); ++ $expected->addPropertyConstraint( ++ 'firstName', new Collection( ++ array('fields' => array( ++ 'foo' => array(new NotNull(), new Range(array('min' => 3))), ++ 'bar' => new Range(array('min' => 5)), ++ )) ++ ) ++ ); ++ $expected->addPropertyConstraint( ++ 'firstName', new Choice( ++ array( ++ 'message' => 'Must be one of %choices%', ++ 'choices' => array('A', 'B'), ++ ) ++ ) ++ ); + $expected->addGetterConstraint('lastName', new NotNull()); + $expected->addGetterConstraint('valid', new IsTrue()); + $expected->addGetterConstraint('permissions', new IsTrue()); +@@ -129,14 +137,22 @@ + $expected->addPropertyConstraint('firstName', new Range(array('min' => 3))); + $expected->addPropertyConstraint('firstName', new All(array(new NotNull(), new Range(array('min' => 3))))); + $expected->addPropertyConstraint('firstName', new All(array('constraints' => array(new NotNull(), new Range(array('min' => 3)))))); +- $expected->addPropertyConstraint('firstName', new Collection(array('fields' => array( +- 'foo' => array(new NotNull(), new Range(array('min' => 3))), +- 'bar' => new Range(array('min' => 5)), +- )))); +- $expected->addPropertyConstraint('firstName', new Choice(array( +- 'message' => 'Must be one of %choices%', +- 'choices' => array('A', 'B'), +- ))); ++ $expected->addPropertyConstraint( ++ 'firstName', new Collection( ++ array('fields' => array( ++ 'foo' => array(new NotNull(), new Range(array('min' => 3))), ++ 'bar' => new Range(array('min' => 5)), ++ )) ++ ) ++ ); ++ $expected->addPropertyConstraint( ++ 'firstName', new Choice( ++ array( ++ 'message' => 'Must be one of %choices%', ++ 'choices' => array('A', 'B'), ++ ) ++ ) ++ ); + $expected->addGetterConstraint('lastName', new NotNull()); + $expected->addGetterConstraint('valid', new IsTrue()); + $expected->addGetterConstraint('permissions', new IsTrue()); + +--- vendor/symfony/validator/Tests/Mapping/Loader/YamlFileLoaderTest.php ++++ PHP_CodeSniffer +@@ -104,14 +104,22 @@ + $expected->addPropertyConstraint('firstName', new Choice(array('A', 'B'))); + $expected->addPropertyConstraint('firstName', new All(array(new NotNull(), new Range(array('min' => 3))))); + $expected->addPropertyConstraint('firstName', new All(array('constraints' => array(new NotNull(), new Range(array('min' => 3)))))); +- $expected->addPropertyConstraint('firstName', new Collection(array('fields' => array( +- 'foo' => array(new NotNull(), new Range(array('min' => 3))), +- 'bar' => array(new Range(array('min' => 5))), +- )))); +- $expected->addPropertyConstraint('firstName', new Choice(array( +- 'message' => 'Must be one of %choices%', +- 'choices' => array('A', 'B'), +- ))); ++ $expected->addPropertyConstraint( ++ 'firstName', new Collection( ++ array('fields' => array( ++ 'foo' => array(new NotNull(), new Range(array('min' => 3))), ++ 'bar' => array(new Range(array('min' => 5))), ++ )) ++ ) ++ ); ++ $expected->addPropertyConstraint( ++ 'firstName', new Choice( ++ array( ++ 'message' => 'Must be one of %choices%', ++ 'choices' => array('A', 'B'), ++ ) ++ ) ++ ); + $expected->addGetterConstraint('lastName', new NotNull()); + $expected->addGetterConstraint('valid', new IsTrue()); + $expected->addGetterConstraint('permissions', new IsTrue()); + +--- vendor/symfony/validator/Tests/Mapping/Loader/FilesLoaderTest.php ++++ PHP_CodeSniffer +@@ -33,12 +33,14 @@ + + public function getFilesLoader(LoaderInterface $loader) + { +- return $this->getMockForAbstractClass('Symfony\Component\Validator\Tests\Fixtures\FilesLoader', array(array( ++ return $this->getMockForAbstractClass( ++ 'Symfony\Component\Validator\Tests\Fixtures\FilesLoader', array(array( + __DIR__.'/constraint-mapping.xml', + __DIR__.'/constraint-mapping.yaml', + __DIR__.'/constraint-mapping.test', + __DIR__.'/constraint-mapping.txt', +- ), $loader)); ++ ), $loader) ++ ); + } + + public function getFileLoader() + +--- vendor/symfony/validator/Tests/Mapping/Loader/XmlFileLoaderTest.php ++++ PHP_CodeSniffer +@@ -62,14 +62,22 @@ + $expected->addPropertyConstraint('firstName', new Choice(array('A', 'B'))); + $expected->addPropertyConstraint('firstName', new All(array(new NotNull(), new Range(array('min' => 3))))); + $expected->addPropertyConstraint('firstName', new All(array('constraints' => array(new NotNull(), new Range(array('min' => 3)))))); +- $expected->addPropertyConstraint('firstName', new Collection(array('fields' => array( +- 'foo' => array(new NotNull(), new Range(array('min' => 3))), +- 'bar' => array(new Range(array('min' => 5))), +- )))); +- $expected->addPropertyConstraint('firstName', new Choice(array( +- 'message' => 'Must be one of %choices%', +- 'choices' => array('A', 'B'), +- ))); ++ $expected->addPropertyConstraint( ++ 'firstName', new Collection( ++ array('fields' => array( ++ 'foo' => array(new NotNull(), new Range(array('min' => 3))), ++ 'bar' => array(new Range(array('min' => 5))), ++ )) ++ ) ++ ); ++ $expected->addPropertyConstraint( ++ 'firstName', new Choice( ++ array( ++ 'message' => 'Must be one of %choices%', ++ 'choices' => array('A', 'B'), ++ ) ++ ) ++ ); + $expected->addGetterConstraint('lastName', new NotNull()); + $expected->addGetterConstraint('valid', new IsTrue()); + $expected->addGetterConstraint('permissions', new IsTrue()); + +--- vendor/symfony/validator/Tests/Mapping/Loader/LoaderChainTest.php ++++ PHP_CodeSniffer +@@ -22,18 +22,20 @@ + + $loader1 = $this->getMock('Symfony\Component\Validator\Mapping\Loader\LoaderInterface'); + $loader1->expects($this->once()) +- ->method('loadClassMetadata') +- ->with($this->equalTo($metadata)); ++ ->method('loadClassMetadata') ++ ->with($this->equalTo($metadata)); + + $loader2 = $this->getMock('Symfony\Component\Validator\Mapping\Loader\LoaderInterface'); + $loader2->expects($this->once()) +- ->method('loadClassMetadata') +- ->with($this->equalTo($metadata)); ++ ->method('loadClassMetadata') ++ ->with($this->equalTo($metadata)); + +- $chain = new LoaderChain(array( ++ $chain = new LoaderChain( ++ array( + $loader1, + $loader2, +- )); ++ ) ++ ); + + $chain->loadClassMetadata($metadata); + } +@@ -44,18 +46,20 @@ + + $loader1 = $this->getMock('Symfony\Component\Validator\Mapping\Loader\LoaderInterface'); + $loader1->expects($this->any()) +- ->method('loadClassMetadata') +- ->will($this->returnValue(true)); ++ ->method('loadClassMetadata') ++ ->will($this->returnValue(true)); + + $loader2 = $this->getMock('Symfony\Component\Validator\Mapping\Loader\LoaderInterface'); + $loader2->expects($this->any()) +- ->method('loadClassMetadata') +- ->will($this->returnValue(false)); ++ ->method('loadClassMetadata') ++ ->will($this->returnValue(false)); + +- $chain = new LoaderChain(array( ++ $chain = new LoaderChain( ++ array( + $loader1, + $loader2, +- )); ++ ) ++ ); + + $this->assertTrue($chain->loadClassMetadata($metadata)); + } +@@ -66,18 +70,20 @@ + + $loader1 = $this->getMock('Symfony\Component\Validator\Mapping\Loader\LoaderInterface'); + $loader1->expects($this->any()) +- ->method('loadClassMetadata') +- ->will($this->returnValue(false)); ++ ->method('loadClassMetadata') ++ ->will($this->returnValue(false)); + + $loader2 = $this->getMock('Symfony\Component\Validator\Mapping\Loader\LoaderInterface'); + $loader2->expects($this->any()) +- ->method('loadClassMetadata') +- ->will($this->returnValue(false)); ++ ->method('loadClassMetadata') ++ ->will($this->returnValue(false)); + +- $chain = new LoaderChain(array( ++ $chain = new LoaderChain( ++ array( + $loader1, + $loader2, +- )); ++ ) ++ ); + + $this->assertFalse($chain->loadClassMetadata($metadata)); + } + +--- vendor/symfony/validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php ++++ PHP_CodeSniffer +@@ -39,20 +39,26 @@ + $metadata = $factory->getMetadataFor(self::CLASSNAME); + + $constraints = array( +- new ConstraintA(array('groups' => array( ++ new ConstraintA( ++ array('groups' => array( + 'Default', + 'EntityParent', + 'Entity', +- ))), +- new ConstraintA(array('groups' => array( ++ )) ++ ), ++ new ConstraintA( ++ array('groups' => array( + 'Default', + 'EntityInterface', + 'Entity', +- ))), +- new ConstraintA(array('groups' => array( ++ )) ++ ), ++ new ConstraintA( ++ array('groups' => array( + 'Default', + 'Entity', +- ))), ++ )) ++ ), + ); + + $this->assertEquals($constraints, $metadata->getConstraints()); +@@ -69,16 +75,20 @@ + ); + + $cache->expects($this->never()) +- ->method('has'); ++ ->method('has'); + $cache->expects($this->once()) +- ->method('read') +- ->with($this->equalTo(self::PARENTCLASS)) +- ->will($this->returnValue(false)); ++ ->method('read') ++ ->with($this->equalTo(self::PARENTCLASS)) ++ ->will($this->returnValue(false)); + $cache->expects($this->once()) +- ->method('write') +- ->will($this->returnCallback(function ($metadata) use ($tester, $constraints) { +- $tester->assertEquals($constraints, $metadata->getConstraints()); +- })); ++ ->method('write') ++ ->will( ++ $this->returnCallback( ++ function ($metadata) use ($tester, $constraints) { ++ $tester->assertEquals($constraints, $metadata->getConstraints()); ++ } ++ ) ++ ); + + $metadata = $factory->getMetadataFor(self::PARENTCLASS); + +@@ -97,13 +107,13 @@ + $metadata->addConstraint(new ConstraintA()); + + $loader->expects($this->never()) +- ->method('loadClassMetadata'); ++ ->method('loadClassMetadata'); + + $cache->expects($this->never()) +- ->method('has'); ++ ->method('has'); + $cache->expects($this->once()) +- ->method('read') +- ->will($this->returnValue($metadata)); ++ ->method('read') ++ ->will($this->returnValue($metadata)); + + $this->assertEquals($metadata, $factory->getMetadataFor(self::PARENTCLASS)); + } + +--- vendor/symfony/validator/Tests/Mapping/ClassMetadataTest.php ++++ PHP_CodeSniffer +@@ -116,15 +116,19 @@ + $this->metadata->addConstraint(new ConstraintA()); + + $constraints = array( +- new ConstraintA(array('groups' => array( ++ new ConstraintA( ++ array('groups' => array( + 'Default', + 'EntityParent', + 'Entity', +- ))), +- new ConstraintA(array('groups' => array( ++ )) ++ ), ++ new ConstraintA( ++ array('groups' => array( + 'Default', + 'Entity', +- ))), ++ )) ++ ), + ); + + $this->assertEquals($constraints, $this->metadata->getConstraints()); +@@ -139,15 +143,19 @@ + $this->metadata->addPropertyConstraint('firstName', new ConstraintA()); + + $constraints = array( +- new ConstraintA(array('groups' => array( ++ new ConstraintA( ++ array('groups' => array( + 'Default', + 'EntityParent', + 'Entity', +- ))), +- new ConstraintA(array('groups' => array( ++ )) ++ ), ++ new ConstraintA( ++ array('groups' => array( + 'Default', + 'Entity', +- ))), ++ )) ++ ), + ); + + $members = $this->metadata->getPropertyMetadata('firstName'); +@@ -174,17 +182,21 @@ + $this->metadata->addPropertyConstraint('internal', new ConstraintA()); + + $parentConstraints = array( +- new ConstraintA(array('groups' => array( ++ new ConstraintA( ++ array('groups' => array( + 'Default', + 'EntityParent', + 'Entity', +- ))), ++ )) ++ ), + ); + $constraints = array( +- new ConstraintA(array('groups' => array( ++ new ConstraintA( ++ array('groups' => array( + 'Default', + 'Entity', +- ))), ++ )) ++ ), + ); + + $members = $this->metadata->getPropertyMetadata('internal'); + +--- vendor/symfony/validator/Tests/Resources/TranslationFilesTest.php ++++ PHP_CodeSniffer +@@ -24,7 +24,9 @@ + public function provideTranslationFiles() + { + return array_map( +- function ($filePath) { return (array) $filePath; }, ++ function ($filePath) { ++ return (array) $filePath; ++ }, + glob(dirname(dirname(__DIR__)).'/Resources/translations/*.xlf') + ); + } + +--- vendor/symfony/validator/Tests/Fixtures/ConstraintA.php ++++ PHP_CodeSniffer +@@ -13,7 +13,9 @@ + + use Symfony\Component\Validator\Constraint; + +-/** @Annotation */ ++/** ++ * @Annotation ++*/ + class ConstraintA extends Constraint + { + public $property1; + +--- vendor/symfony/validator/Tests/Fixtures/ConstraintB.php ++++ PHP_CodeSniffer +@@ -13,7 +13,9 @@ + + use Symfony\Component\Validator\Constraint; + +-/** @Annotation */ ++/** ++ * @Annotation ++*/ + class ConstraintB extends Constraint + { + public function getTargets() + +--- vendor/symfony/validator/Tests/Fixtures/ConstraintWithValue.php ++++ PHP_CodeSniffer +@@ -13,7 +13,9 @@ + + use Symfony\Component\Validator\Constraint; + +-/** @Annotation */ ++/** ++ * @Annotation ++*/ + class ConstraintWithValue extends Constraint + { + public $property; + +--- vendor/symfony/validator/Tests/Fixtures/ConstraintWithValueAsDefault.php ++++ PHP_CodeSniffer +@@ -13,7 +13,9 @@ + + use Symfony\Component\Validator\Constraint; + +-/** @Annotation */ ++/** ++ * @Annotation ++*/ + class ConstraintWithValueAsDefault extends Constraint + { + public $property; + +--- vendor/symfony/validator/Tests/Fixtures/ConstraintC.php ++++ PHP_CodeSniffer +@@ -13,7 +13,9 @@ + + use Symfony\Component\Validator\Constraint; + +-/** @Annotation */ ++/** ++ * @Annotation ++*/ + class ConstraintC extends Constraint + { + public $option1; + +--- vendor/symfony/validator/Tests/Validator/Abstract2Dot5ApiTest.php ++++ PHP_CodeSniffer +@@ -29,7 +29,7 @@ + /** + * Verifies that a validator satisfies the API of Symfony 2.5+. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + */ +@@ -93,18 +93,31 @@ + $context->addViolation('Message 2'); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => function () {}, +- 'groups' => 'Group 1', +- ))); +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Group 2', +- ))); +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group 3', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => function () { ++ }, ++ 'groups' => 'Group 1', ++ ) ++ ) ++ ); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Group 2', ++ ) ++ ) ++ ); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group 3', ++ ) ++ ) ++ ); + + $sequence = new GroupSequence(array('Group 1', 'Group 2', 'Group 3')); + $violations = $this->validator->validate($entity, new Valid(), $sequence); +@@ -127,14 +140,22 @@ + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Group 1', +- ))); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group 2', +- ))); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Group 1', ++ ) ++ ) ++ ); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group 2', ++ ) ++ ) ++ ); + + $sequence = new GroupSequence(array('Group 1', 'Entity')); + $violations = $this->validator->validate($entity, new Valid(), $sequence); +@@ -155,8 +176,7 @@ + ->getValidator() + // Since the validator is not context aware, the group must + // be passed explicitly +- ->validate($value->reference, new Valid(), 'Group') +- ; ++ ->validate($value->reference, new Valid(), 'Group'); + + /* @var ConstraintViolationInterface[] $violations */ + $test->assertCount(1, $violations); +@@ -187,14 +207,22 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Group', +- ))); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validator->validate($entity, new Valid(), 'Group'); + +@@ -220,8 +248,7 @@ + ->getValidator() + ->inContext($context) + ->atPath('subpath') +- ->validate($value->reference) +- ; ++ ->validate($value->reference); + + // context changes shouldn't leak out of the validate() call + $test->assertSame($previousValue, $context->getValue()); +@@ -244,14 +271,22 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Group', +- ))); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validator->validate($entity, new Valid(), 'Group'); + +@@ -284,8 +319,7 @@ + ->getValidator() + ->inContext($context) + ->atPath('subpath') +- ->validate(array('key' => $value->reference)) +- ; ++ ->validate(array('key' => $value->reference)); + + // context changes shouldn't leak out of the validate() call + $test->assertSame($previousValue, $context->getValue()); +@@ -308,14 +342,22 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Group', +- ))); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validator->validate($entity, new Valid(), 'Group'); + +@@ -351,10 +393,14 @@ + }; + + $this->metadataFactory->addMetadata(new ClassMetadata('ArrayIterator')); +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($traversable, new Valid(), 'Group'); + +@@ -383,10 +429,14 @@ + $traversableMetadata->addConstraint(new Traverse(true)); + + $this->metadataFactory->addMetadata($traversableMetadata); +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($traversable, new Valid(), 'Group'); + +@@ -408,10 +458,14 @@ + $traversableMetadata->addConstraint(new Traverse(false)); + + $this->metadataFactory->addMetadata($traversableMetadata); +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($traversable, new Valid(), 'Group'); + +@@ -445,10 +499,14 @@ + $traversableMetadata->addConstraint(new Traverse(false)); + + $this->metadataFactory->addMetadata($traversableMetadata); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + $this->metadata->addPropertyConstraint('reference', new Valid()); + + $violations = $this->validate($entity, new Valid(), 'Group'); +@@ -471,13 +529,21 @@ + $traversableMetadata->addConstraint(new Traverse(false)); + + $this->metadataFactory->addMetadata($traversableMetadata); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); +- $this->metadata->addPropertyConstraint('reference', new Valid(array( +- 'traverse' => true, +- ))); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); ++ $this->metadata->addPropertyConstraint( ++ 'reference', new Valid( ++ array( ++ 'traverse' => true, ++ ) ++ ) ++ ); + + $violations = $this->validate($entity, new Valid(), 'Group'); + +@@ -499,13 +565,21 @@ + $traversableMetadata->addConstraint(new Traverse(true)); + + $this->metadataFactory->addMetadata($traversableMetadata); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); +- $this->metadata->addPropertyConstraint('reference', new Valid(array( +- 'traverse' => false, +- ))); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); ++ $this->metadata->addPropertyConstraint( ++ 'reference', new Valid( ++ array( ++ 'traverse' => false, ++ ) ++ ) ++ ); + + $violations = $this->validate($entity, new Valid(), 'Group'); + +@@ -607,10 +681,14 @@ + $context->addViolation('Message'); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => array('Group 1', 'Group 2'), +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => array('Group 1', 'Group 2'), ++ ) ++ ) ++ ); + + $violations = $this->validator->validate($entity, new Valid(), array('Group 1', 'Group 2')); + +@@ -626,10 +704,14 @@ + $context->addViolation('Message'); + }; + +- $this->metadata->addPropertyConstraint('firstName', new Callback(array( +- 'callback' => $callback, +- 'groups' => array('Group 1', 'Group 2'), +- ))); ++ $this->metadata->addPropertyConstraint( ++ 'firstName', new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => array('Group 1', 'Group 2'), ++ ) ++ ) ++ ); + + $violations = $this->validator->validate($entity, new Valid(), array('Group 1', 'Group 2')); + +@@ -678,18 +760,24 @@ + $initializer1->expects($this->once()) + ->method('initialize') + ->with($entity) +- ->will($this->returnCallback(function ($object) { +- $object->initialized = true; +- })); ++ ->will( ++ $this->returnCallback( ++ function ($object) { ++ $object->initialized = true; ++ } ++ ) ++ ); + + $initializer2->expects($this->once()) + ->method('initialize') + ->with($entity); + +- $this->validator = $this->createValidator($this->metadataFactory, array( ++ $this->validator = $this->createValidator( ++ $this->metadataFactory, array( + $initializer1, + $initializer2, +- )); ++ ) ++ ); + + // prepare constraint which + // * checks that "initialized" is set to true + +--- vendor/symfony/validator/Tests/Validator/AbstractLegacyApiTest.php ++++ PHP_CodeSniffer +@@ -23,10 +23,10 @@ + /** + * Verifies that a validator satisfies the API of Symfony < 2.5. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek +- * @group legacy ++ * @group legacy + */ + abstract class AbstractLegacyApiTest extends AbstractValidatorTest + { +@@ -85,10 +85,14 @@ + $test->fail('Should not be called'); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $this->validator->validate($traversable, 'Group'); + } +@@ -100,18 +104,24 @@ + { + $test = $this; + $entity = new Entity(); +- $traversable = new \ArrayIterator(array( ++ $traversable = new \ArrayIterator( ++ array( + 2 => new \ArrayIterator(array('key' => $entity)), +- )); ++ ) ++ ); + + $callback = function () use ($test) { + $test->fail('Should not be called'); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $this->validator->validate($traversable, 'Group'); + } +@@ -151,14 +161,22 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Group', +- ))); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validator->validate($entity, 'Group'); + +@@ -209,14 +227,22 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Group', +- ))); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validator->validate($entity, 'Group'); + +@@ -275,18 +301,24 @@ + $initializer1->expects($this->once()) + ->method('initialize') + ->with($entity) +- ->will($this->returnCallback(function ($object) { +- $object->initialized = true; +- })); ++ ->will( ++ $this->returnCallback( ++ function ($object) { ++ $object->initialized = true; ++ } ++ ) ++ ); + + $initializer2->expects($this->once()) + ->method('initialize') + ->with($entity); + +- $this->validator = $this->createValidator($this->metadataFactory, array( ++ $this->validator = $this->createValidator( ++ $this->metadataFactory, array( + $initializer1, + $initializer2, +- )); ++ ) ++ ); + + // prepare constraint which + // * checks that "initialized" is set to true + +--- vendor/symfony/validator/Tests/Validator/AbstractValidatorTest.php ++++ PHP_CodeSniffer +@@ -86,10 +86,12 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $constraint = new Callback(array( ++ $constraint = new Callback( ++ array( + 'callback' => $callback, + 'groups' => 'Group', +- )); ++ ) ++ ); + + $violations = $this->validate('Bernhard', $constraint, 'Group'); + +@@ -123,10 +125,14 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($entity, null, 'Group'); + +@@ -163,10 +169,14 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addPropertyConstraint('firstName', new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addPropertyConstraint( ++ 'firstName', new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($entity, null, 'Group'); + +@@ -203,10 +213,14 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addGetterConstraint('lastName', new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addGetterConstraint( ++ 'lastName', new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($entity, null, 'Group'); + +@@ -241,10 +255,14 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($array, null, 'Group'); + +@@ -279,10 +297,14 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($array, null, 'Group'); + +@@ -317,10 +339,14 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($traversable, null, 'Group'); + +@@ -340,9 +366,11 @@ + { + $test = $this; + $entity = new Entity(); +- $traversable = new \ArrayIterator(array( ++ $traversable = new \ArrayIterator( ++ array( + 2 => new \ArrayIterator(array('key' => $entity)), +- )); ++ ) ++ ); + + $callback = function ($value, ExecutionContextInterface $context) use ($test, $entity, $traversable) { + $test->assertSame($test::ENTITY_CLASS, $context->getClassName()); +@@ -357,10 +385,14 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($traversable, null, 'Group'); + +@@ -396,10 +428,14 @@ + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($entity, null, 'Group'); + +@@ -438,10 +474,14 @@ + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); +- $this->referenceMetadata->addPropertyConstraint('value', new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->referenceMetadata->addPropertyConstraint( ++ 'value', new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($entity, null, 'Group'); + +@@ -480,10 +520,14 @@ + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); +- $this->referenceMetadata->addPropertyConstraint('privateValue', new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->referenceMetadata->addPropertyConstraint( ++ 'privateValue', new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($entity, null, 'Group'); + +@@ -545,10 +589,14 @@ + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($entity, null, 'Group'); + +@@ -585,10 +633,14 @@ + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($entity, null, 'Group'); + +@@ -613,9 +665,13 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addPropertyConstraint('reference', new Valid(array( +- 'traverse' => false, +- ))); ++ $this->metadata->addPropertyConstraint( ++ 'reference', new Valid( ++ array( ++ 'traverse' => false, ++ ) ++ ) ++ ); + $this->referenceMetadata->addConstraint(new Callback($callback)); + + $violations = $this->validate($entity); +@@ -633,9 +689,13 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addPropertyConstraint('reference', new Valid(array( +- 'traverse' => false, +- ))); ++ $this->metadata->addPropertyConstraint( ++ 'reference', new Valid( ++ array( ++ 'traverse' => false, ++ ) ++ ) ++ ); + $this->referenceMetadata->addConstraint(new Callback($callback)); + + $violations = $this->validate($entity); +@@ -690,10 +750,14 @@ + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($entity, null, 'Group'); + +@@ -719,9 +783,13 @@ + }; + + $this->metadataFactory->addMetadata(new ClassMetadata('ArrayIterator')); +- $this->metadata->addPropertyConstraint('reference', new Valid(array( +- 'traverse' => false, +- ))); ++ $this->metadata->addPropertyConstraint( ++ 'reference', new Valid( ++ array( ++ 'traverse' => false, ++ ) ++ ) ++ ); + $this->referenceMetadata->addConstraint(new Callback($callback)); + + $violations = $this->validate($entity); +@@ -738,9 +806,13 @@ + $entity = new Entity(); + $entity->reference = new \ArrayIterator(); + +- $this->metadata->addPropertyConstraint('reference', new Valid(array( +- 'traverse' => false, +- ))); ++ $this->metadata->addPropertyConstraint( ++ 'reference', new Valid( ++ array( ++ 'traverse' => false, ++ ) ++ ) ++ ); + + $this->validate($entity); + } +@@ -749,9 +821,11 @@ + { + $test = $this; + $entity = new Entity(); +- $entity->reference = new \ArrayIterator(array( ++ $entity->reference = new \ArrayIterator( ++ array( + 2 => new \ArrayIterator(array('key' => new Reference())), +- )); ++ ) ++ ); + + $callback = function ($value, ExecutionContextInterface $context) use ($test, $entity) { + $test->assertSame($test::REFERENCE_CLASS, $context->getClassName()); +@@ -766,13 +840,21 @@ + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + +- $this->metadata->addPropertyConstraint('reference', new Valid(array( +- 'traverse' => true, +- ))); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addPropertyConstraint( ++ 'reference', new Valid( ++ array( ++ 'traverse' => true, ++ ) ++ ) ++ ); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validate($entity, null, 'Group'); + +@@ -814,14 +896,22 @@ + $context->addViolation('Other violation'); + }; + +- $this->metadata->addPropertyConstraint('firstName', new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Group', +- ))); +- $this->metadata->addPropertyConstraint('lastName', new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addPropertyConstraint( ++ 'firstName', new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); ++ $this->metadata->addPropertyConstraint( ++ 'lastName', new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validateProperty($entity, 'firstName', 'Group'); + +@@ -841,7 +931,7 @@ + * Cannot be UnsupportedMetadataException for BC with Symfony < 2.5. + * + * @expectedException \Symfony\Component\Validator\Exception\ValidatorException +- * @group legacy ++ * @group legacy + */ + public function testLegacyValidatePropertyFailsIfPropertiesNotSupported() + { +@@ -889,14 +979,22 @@ + $context->addViolation('Other violation'); + }; + +- $this->metadata->addPropertyConstraint('firstName', new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Group', +- ))); +- $this->metadata->addPropertyConstraint('lastName', new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addPropertyConstraint( ++ 'firstName', new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); ++ $this->metadata->addPropertyConstraint( ++ 'lastName', new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validatePropertyValue( + $entity, +@@ -940,14 +1038,22 @@ + $context->addViolation('Other violation'); + }; + +- $this->metadata->addPropertyConstraint('firstName', new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Group', +- ))); +- $this->metadata->addPropertyConstraint('lastName', new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group', +- ))); ++ $this->metadata->addPropertyConstraint( ++ 'firstName', new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); ++ $this->metadata->addPropertyConstraint( ++ 'lastName', new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group', ++ ) ++ ) ++ ); + + $violations = $this->validatePropertyValue( + self::ENTITY_CLASS, +@@ -972,7 +1078,7 @@ + * Cannot be UnsupportedMetadataException for BC with Symfony < 2.5. + * + * @expectedException \Symfony\Component\Validator\Exception\ValidatorException +- * @group legacy ++ * @group legacy + */ + public function testLegacyValidatePropertyValueFailsIfPropertiesNotSupported() + { +@@ -1043,14 +1149,22 @@ + $context->addViolation('Message'); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group 1', +- ))); +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group 2', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group 1', ++ ) ++ ) ++ ); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group 2', ++ ) ++ ) ++ ); + + $violations = $this->validate($entity, null, 'Group 2'); + +@@ -1066,14 +1180,22 @@ + $context->addViolation('Message'); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group 1', +- ))); +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback, +- 'groups' => 'Group 2', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group 1', ++ ) ++ ) ++ ); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback, ++ 'groups' => 'Group 2', ++ ) ++ ) ++ ); + + $violations = $this->validate($entity, null, array('Group 1', 'Group 2')); + +@@ -1092,18 +1214,31 @@ + $context->addViolation('Violation in Group 3'); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => function () {}, +- 'groups' => 'Group 1', +- ))); +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Group 2', +- ))); +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group 3', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => function () { ++ }, ++ 'groups' => 'Group 1', ++ ) ++ ) ++ ); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Group 2', ++ ) ++ ) ++ ); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group 3', ++ ) ++ ) ++ ); + + $sequence = new GroupSequence(array('Group 1', 'Group 2', 'Group 3', 'Entity')); + $this->metadata->setGroupSequence($sequence); +@@ -1126,18 +1261,31 @@ + $context->addViolation('Violation in Group 3'); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => function () {}, +- 'groups' => 'Group 1', +- ))); +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Group 2', +- ))); +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group 3', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => function () { ++ }, ++ 'groups' => 'Group 1', ++ ) ++ ) ++ ); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Group 2', ++ ) ++ ) ++ ); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group 3', ++ ) ++ ) ++ ); + + $sequence = array('Group 1', 'Group 2', 'Group 3', 'Entity'); + $this->metadata->setGroupSequence($sequence); +@@ -1162,14 +1310,22 @@ + }; + + $this->metadata->addPropertyConstraint('reference', new Valid()); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Default', +- ))); +- $this->referenceMetadata->addConstraint(new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group 1', +- ))); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Default', ++ ) ++ ) ++ ); ++ $this->referenceMetadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group 1', ++ ) ++ ) ++ ); + + $sequence = new GroupSequence(array('Group 1', 'Entity')); + $this->metadata->setGroupSequence($sequence); +@@ -1192,14 +1348,22 @@ + $context->addViolation('Violation in group sequence'); + }; + +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Other Group', +- ))); +- $this->metadata->addConstraint(new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group 1', +- ))); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Other Group', ++ ) ++ ) ++ ); ++ $this->metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group 1', ++ ) ++ ) ++ ); + + $sequence = new GroupSequence(array('Group 1', 'Entity')); + $this->metadata->setGroupSequence($sequence); +@@ -1224,18 +1388,31 @@ + }; + + $metadata = new ClassMetadata(get_class($entity)); +- $metadata->addConstraint(new Callback(array( +- 'callback' => function () {}, +- 'groups' => 'Group 1', +- ))); +- $metadata->addConstraint(new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Group 2', +- ))); +- $metadata->addConstraint(new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group 3', +- ))); ++ $metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => function () { ++ }, ++ 'groups' => 'Group 1', ++ ) ++ ) ++ ); ++ $metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Group 2', ++ ) ++ ) ++ ); ++ $metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group 3', ++ ) ++ ) ++ ); + $metadata->setGroupSequenceProvider(true); + + $this->metadataFactory->addMetadata($metadata); +@@ -1260,18 +1437,31 @@ + }; + + $metadata = new ClassMetadata(get_class($entity)); +- $metadata->addConstraint(new Callback(array( +- 'callback' => function () {}, +- 'groups' => 'Group 1', +- ))); +- $metadata->addConstraint(new Callback(array( +- 'callback' => $callback1, +- 'groups' => 'Group 2', +- ))); +- $metadata->addConstraint(new Callback(array( +- 'callback' => $callback2, +- 'groups' => 'Group 3', +- ))); ++ $metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => function () { ++ }, ++ 'groups' => 'Group 1', ++ ) ++ ) ++ ); ++ $metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback1, ++ 'groups' => 'Group 2', ++ ) ++ ) ++ ); ++ $metadata->addConstraint( ++ new Callback( ++ array( ++ 'callback' => $callback2, ++ 'groups' => 'Group 3', ++ ) ++ ) ++ ); + $metadata->setGroupSequenceProvider(true); + + $this->metadataFactory->addMetadata($metadata); + +--- vendor/symfony/validator/Constraints/Iban.php ++++ PHP_CodeSniffer +@@ -23,11 +23,15 @@ + */ + class Iban extends Constraint + { +- /** @deprecated, to be removed in 3.0. */ ++ /** ++ * @deprecated, to be removed in 3.0. ++*/ + const TOO_SHORT_ERROR = '88e5e319-0aeb-4979-a27e-3d9ce0c16166'; + const INVALID_COUNTRY_CODE_ERROR = 'de78ee2c-bd50-44e2-aec8-3d8228aeadb9'; + const INVALID_CHARACTERS_ERROR = '8d3d85e4-784f-4719-a5bc-d9e40d45a3a5'; +- /** @deprecated, to be removed in 3.0. */ ++ /** ++ * @deprecated, to be removed in 3.0. ++*/ + const INVALID_CASE_ERROR = 'f4bf62fe-03ec-42af-a53b-68e21b1e7274'; + const CHECKSUM_FAILED_ERROR = 'b9401321-f9bf-4dcb-83c1-f31094440795'; + const INVALID_FORMAT_ERROR = 'c8d318f1-2ecc-41ba-b983-df70d225cf5a'; + +--- vendor/symfony/validator/Constraints/IpValidator.php ++++ PHP_CodeSniffer +@@ -44,53 +44,53 @@ + $value = (string) $value; + + switch ($constraint->version) { +- case Ip::V4: +- $flag = FILTER_FLAG_IPV4; +- break; +- +- case Ip::V6: +- $flag = FILTER_FLAG_IPV6; +- break; +- +- case Ip::V4_NO_PRIV: +- $flag = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE; +- break; +- +- case Ip::V6_NO_PRIV: +- $flag = FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE; +- break; +- +- case Ip::ALL_NO_PRIV: +- $flag = FILTER_FLAG_NO_PRIV_RANGE; +- break; +- +- case Ip::V4_NO_RES: +- $flag = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_RES_RANGE; +- break; +- +- case Ip::V6_NO_RES: +- $flag = FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE; +- break; +- +- case Ip::ALL_NO_RES: +- $flag = FILTER_FLAG_NO_RES_RANGE; +- break; +- +- case Ip::V4_ONLY_PUBLIC: +- $flag = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE; +- break; +- +- case Ip::V6_ONLY_PUBLIC: +- $flag = FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE; +- break; +- +- case Ip::ALL_ONLY_PUBLIC: +- $flag = FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE; +- break; +- +- default: +- $flag = null; +- break; ++ case Ip::V4: ++ $flag = FILTER_FLAG_IPV4; ++ break; ++ ++ case Ip::V6: ++ $flag = FILTER_FLAG_IPV6; ++ break; ++ ++ case Ip::V4_NO_PRIV: ++ $flag = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE; ++ break; ++ ++ case Ip::V6_NO_PRIV: ++ $flag = FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE; ++ break; ++ ++ case Ip::ALL_NO_PRIV: ++ $flag = FILTER_FLAG_NO_PRIV_RANGE; ++ break; ++ ++ case Ip::V4_NO_RES: ++ $flag = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_RES_RANGE; ++ break; ++ ++ case Ip::V6_NO_RES: ++ $flag = FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE; ++ break; ++ ++ case Ip::ALL_NO_RES: ++ $flag = FILTER_FLAG_NO_RES_RANGE; ++ break; ++ ++ case Ip::V4_ONLY_PUBLIC: ++ $flag = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE; ++ break; ++ ++ case Ip::V6_ONLY_PUBLIC: ++ $flag = FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE; ++ break; ++ ++ case Ip::ALL_ONLY_PUBLIC: ++ $flag = FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE; ++ break; ++ ++ default: ++ $flag = null; ++ break; + } + + if (!filter_var($value, FILTER_VALIDATE_IP, $flag)) { + +--- vendor/symfony/validator/Constraints/Valid.php ++++ PHP_CodeSniffer +@@ -32,10 +32,12 @@ + public function __construct($options = null) + { + if (is_array($options) && array_key_exists('groups', $options)) { +- throw new ConstraintDefinitionException(sprintf( +- 'The option "groups" is not supported by the constraint %s', +- __CLASS__ +- )); ++ throw new ConstraintDefinitionException( ++ sprintf( ++ 'The option "groups" is not supported by the constraint %s', ++ __CLASS__ ++ ) ++ ); + } + + if (is_array($options) && array_key_exists('deep', $options)) { + +--- vendor/symfony/validator/Constraints/FileValidator.php ++++ PHP_CodeSniffer +@@ -51,116 +51,116 @@ + + if ($value instanceof UploadedFile && !$value->isValid()) { + switch ($value->getError()) { +- case UPLOAD_ERR_INI_SIZE: +- $iniLimitSize = UploadedFile::getMaxFilesize(); +- if ($constraint->maxSize && $constraint->maxSize < $iniLimitSize) { +- $limitInBytes = $constraint->maxSize; +- $binaryFormat = $constraint->binaryFormat; +- } else { +- $limitInBytes = $iniLimitSize; +- $binaryFormat = true; +- } ++ case UPLOAD_ERR_INI_SIZE: ++ $iniLimitSize = UploadedFile::getMaxFilesize(); ++ if ($constraint->maxSize && $constraint->maxSize < $iniLimitSize) { ++ $limitInBytes = $constraint->maxSize; ++ $binaryFormat = $constraint->binaryFormat; ++ } else { ++ $limitInBytes = $iniLimitSize; ++ $binaryFormat = true; ++ } + +- list($sizeAsString, $limitAsString, $suffix) = $this->factorizeSizes(0, $limitInBytes, $binaryFormat); +- if ($this->context instanceof ExecutionContextInterface) { +- $this->context->buildViolation($constraint->uploadIniSizeErrorMessage) +- ->setParameter('{{ limit }}', $limitAsString) +- ->setParameter('{{ suffix }}', $suffix) +- ->setCode(UPLOAD_ERR_INI_SIZE) +- ->addViolation(); +- } else { +- $this->buildViolation($constraint->uploadIniSizeErrorMessage) +- ->setParameter('{{ limit }}', $limitAsString) +- ->setParameter('{{ suffix }}', $suffix) +- ->setCode(UPLOAD_ERR_INI_SIZE) +- ->addViolation(); +- } ++ list($sizeAsString, $limitAsString, $suffix) = $this->factorizeSizes(0, $limitInBytes, $binaryFormat); ++ if ($this->context instanceof ExecutionContextInterface) { ++ $this->context->buildViolation($constraint->uploadIniSizeErrorMessage) ++ ->setParameter('{{ limit }}', $limitAsString) ++ ->setParameter('{{ suffix }}', $suffix) ++ ->setCode(UPLOAD_ERR_INI_SIZE) ++ ->addViolation(); ++ } else { ++ $this->buildViolation($constraint->uploadIniSizeErrorMessage) ++ ->setParameter('{{ limit }}', $limitAsString) ++ ->setParameter('{{ suffix }}', $suffix) ++ ->setCode(UPLOAD_ERR_INI_SIZE) ++ ->addViolation(); ++ } + +- return; +- case UPLOAD_ERR_FORM_SIZE: +- if ($this->context instanceof ExecutionContextInterface) { +- $this->context->buildViolation($constraint->uploadFormSizeErrorMessage) +- ->setCode(UPLOAD_ERR_FORM_SIZE) +- ->addViolation(); +- } else { +- $this->buildViolation($constraint->uploadFormSizeErrorMessage) +- ->setCode(UPLOAD_ERR_FORM_SIZE) +- ->addViolation(); +- } ++ return; ++ case UPLOAD_ERR_FORM_SIZE: ++ if ($this->context instanceof ExecutionContextInterface) { ++ $this->context->buildViolation($constraint->uploadFormSizeErrorMessage) ++ ->setCode(UPLOAD_ERR_FORM_SIZE) ++ ->addViolation(); ++ } else { ++ $this->buildViolation($constraint->uploadFormSizeErrorMessage) ++ ->setCode(UPLOAD_ERR_FORM_SIZE) ++ ->addViolation(); ++ } + +- return; +- case UPLOAD_ERR_PARTIAL: +- if ($this->context instanceof ExecutionContextInterface) { +- $this->context->buildViolation($constraint->uploadPartialErrorMessage) +- ->setCode(UPLOAD_ERR_PARTIAL) +- ->addViolation(); +- } else { +- $this->buildViolation($constraint->uploadPartialErrorMessage) +- ->setCode(UPLOAD_ERR_PARTIAL) +- ->addViolation(); +- } ++ return; ++ case UPLOAD_ERR_PARTIAL: ++ if ($this->context instanceof ExecutionContextInterface) { ++ $this->context->buildViolation($constraint->uploadPartialErrorMessage) ++ ->setCode(UPLOAD_ERR_PARTIAL) ++ ->addViolation(); ++ } else { ++ $this->buildViolation($constraint->uploadPartialErrorMessage) ++ ->setCode(UPLOAD_ERR_PARTIAL) ++ ->addViolation(); ++ } + +- return; +- case UPLOAD_ERR_NO_FILE: +- if ($this->context instanceof ExecutionContextInterface) { +- $this->context->buildViolation($constraint->uploadNoFileErrorMessage) +- ->setCode(UPLOAD_ERR_NO_FILE) +- ->addViolation(); +- } else { +- $this->buildViolation($constraint->uploadNoFileErrorMessage) +- ->setCode(UPLOAD_ERR_NO_FILE) +- ->addViolation(); +- } ++ return; ++ case UPLOAD_ERR_NO_FILE: ++ if ($this->context instanceof ExecutionContextInterface) { ++ $this->context->buildViolation($constraint->uploadNoFileErrorMessage) ++ ->setCode(UPLOAD_ERR_NO_FILE) ++ ->addViolation(); ++ } else { ++ $this->buildViolation($constraint->uploadNoFileErrorMessage) ++ ->setCode(UPLOAD_ERR_NO_FILE) ++ ->addViolation(); ++ } + +- return; +- case UPLOAD_ERR_NO_TMP_DIR: +- if ($this->context instanceof ExecutionContextInterface) { +- $this->context->buildViolation($constraint->uploadNoTmpDirErrorMessage) +- ->setCode(UPLOAD_ERR_NO_TMP_DIR) +- ->addViolation(); +- } else { +- $this->buildViolation($constraint->uploadNoTmpDirErrorMessage) +- ->setCode(UPLOAD_ERR_NO_TMP_DIR) +- ->addViolation(); +- } ++ return; ++ case UPLOAD_ERR_NO_TMP_DIR: ++ if ($this->context instanceof ExecutionContextInterface) { ++ $this->context->buildViolation($constraint->uploadNoTmpDirErrorMessage) ++ ->setCode(UPLOAD_ERR_NO_TMP_DIR) ++ ->addViolation(); ++ } else { ++ $this->buildViolation($constraint->uploadNoTmpDirErrorMessage) ++ ->setCode(UPLOAD_ERR_NO_TMP_DIR) ++ ->addViolation(); ++ } + +- return; +- case UPLOAD_ERR_CANT_WRITE: +- if ($this->context instanceof ExecutionContextInterface) { +- $this->context->buildViolation($constraint->uploadCantWriteErrorMessage) +- ->setCode(UPLOAD_ERR_CANT_WRITE) +- ->addViolation(); +- } else { +- $this->buildViolation($constraint->uploadCantWriteErrorMessage) +- ->setCode(UPLOAD_ERR_CANT_WRITE) +- ->addViolation(); +- } ++ return; ++ case UPLOAD_ERR_CANT_WRITE: ++ if ($this->context instanceof ExecutionContextInterface) { ++ $this->context->buildViolation($constraint->uploadCantWriteErrorMessage) ++ ->setCode(UPLOAD_ERR_CANT_WRITE) ++ ->addViolation(); ++ } else { ++ $this->buildViolation($constraint->uploadCantWriteErrorMessage) ++ ->setCode(UPLOAD_ERR_CANT_WRITE) ++ ->addViolation(); ++ } + +- return; +- case UPLOAD_ERR_EXTENSION: +- if ($this->context instanceof ExecutionContextInterface) { +- $this->context->buildViolation($constraint->uploadExtensionErrorMessage) +- ->setCode(UPLOAD_ERR_EXTENSION) +- ->addViolation(); +- } else { +- $this->buildViolation($constraint->uploadExtensionErrorMessage) +- ->setCode(UPLOAD_ERR_EXTENSION) +- ->addViolation(); +- } ++ return; ++ case UPLOAD_ERR_EXTENSION: ++ if ($this->context instanceof ExecutionContextInterface) { ++ $this->context->buildViolation($constraint->uploadExtensionErrorMessage) ++ ->setCode(UPLOAD_ERR_EXTENSION) ++ ->addViolation(); ++ } else { ++ $this->buildViolation($constraint->uploadExtensionErrorMessage) ++ ->setCode(UPLOAD_ERR_EXTENSION) ++ ->addViolation(); ++ } + +- return; +- default: +- if ($this->context instanceof ExecutionContextInterface) { +- $this->context->buildViolation($constraint->uploadErrorMessage) +- ->setCode($value->getError()) +- ->addViolation(); +- } else { +- $this->buildViolation($constraint->uploadErrorMessage) +- ->setCode($value->getError()) +- ->addViolation(); +- } ++ return; ++ default: ++ if ($this->context instanceof ExecutionContextInterface) { ++ $this->context->buildViolation($constraint->uploadErrorMessage) ++ ->setCode($value->getError()) ++ ->addViolation(); ++ } else { ++ $this->buildViolation($constraint->uploadErrorMessage) ++ ->setCode($value->getError()) ++ ->addViolation(); ++ } + +- return; ++ return; + } + } + + +--- vendor/symfony/validator/Constraints/ImageValidator.php ++++ PHP_CodeSniffer +@@ -47,7 +47,8 @@ + if (null === $constraint->minWidth && null === $constraint->maxWidth + && null === $constraint->minHeight && null === $constraint->maxHeight + && null === $constraint->minRatio && null === $constraint->maxRatio +- && $constraint->allowSquare && $constraint->allowLandscape && $constraint->allowPortrait) { ++ && $constraint->allowSquare && $constraint->allowLandscape && $constraint->allowPortrait ++ ) { + return; + } + + +--- vendor/symfony/validator/Constraints/LuhnValidator.php ++++ PHP_CodeSniffer +@@ -22,7 +22,7 @@ + * For a list of example card numbers that are used to test this + * class, please see the LuhnValidatorTest class. + * +- * @see http://en.wikipedia.org/wiki/Luhn_algorithm ++ * @see http://en.wikipedia.org/wiki/Luhn_algorithm + * + * @author Tim Nagel + * @author Greg Knapp http://gregk.me/2011/php-implementation-of-bank-card-luhn-algorithm/ + +--- vendor/symfony/validator/Constraints/Collection.php ++++ PHP_CodeSniffer +@@ -42,7 +42,8 @@ + { + // no known options set? $options is the fields array + if (is_array($options) +- && !array_intersect(array_keys($options), array('groups', 'fields', 'allowExtraFields', 'allowMissingFields', 'extraFieldsMessage', 'missingFieldsMessage'))) { ++ && !array_intersect(array_keys($options), array('groups', 'fields', 'allowExtraFields', 'allowMissingFields', 'extraFieldsMessage', 'missingFieldsMessage')) ++ ) { + $options = array('fields' => $options); + } + + +--- vendor/symfony/validator/Constraints/AbstractComparison.php ++++ PHP_CodeSniffer +@@ -31,10 +31,12 @@ + public function __construct($options = null) + { + if (is_array($options) && !isset($options['value'])) { +- throw new ConstraintDefinitionException(sprintf( +- 'The %s constraint requires the "value" option to be set.', +- get_class($this) +- )); ++ throw new ConstraintDefinitionException( ++ sprintf( ++ 'The %s constraint requires the "value" option to be set.', ++ get_class($this) ++ ) ++ ); + } + + parent::__construct($options); + +--- vendor/symfony/validator/Constraints/GroupSequence.php ++++ PHP_CodeSniffer +@@ -99,7 +99,7 @@ + * + * @return \Traversable The iterator + * +- * @see \IteratorAggregate::getIterator() ++ * @see \IteratorAggregate::getIterator() + * @deprecated since version 2.5, to be removed in 3.0. + */ + public function getIterator() +@@ -145,10 +145,12 @@ + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.5 and will be removed in 3.0.', E_USER_DEPRECATED); + + if (!isset($this->groups[$offset])) { +- throw new OutOfBoundsException(sprintf( +- 'The offset "%s" does not exist.', +- $offset +- )); ++ throw new OutOfBoundsException( ++ sprintf( ++ 'The offset "%s" does not exist.', ++ $offset ++ ) ++ ); + } + + return $this->groups[$offset]; + +--- vendor/symfony/validator/Constraints/Traverse.php ++++ PHP_CodeSniffer +@@ -28,10 +28,12 @@ + public function __construct($options = null) + { + if (is_array($options) && array_key_exists('groups', $options)) { +- throw new ConstraintDefinitionException(sprintf( +- 'The option "groups" is not supported by the constraint %s', +- __CLASS__ +- )); ++ throw new ConstraintDefinitionException( ++ sprintf( ++ 'The option "groups" is not supported by the constraint %s', ++ __CLASS__ ++ ) ++ ); + } + + parent::__construct($options); + +--- vendor/symfony/validator/Constraints/UuidValidator.php ++++ PHP_CodeSniffer +@@ -232,20 +232,20 @@ + if ($i !== $h) { + if ($this->context instanceof ExecutionContextInterface) { + $this->context->buildViolation($constraint->message) +- ->setParameter( +- '{{ value }}', +- $this->formatValue($value) +- ) +- ->setCode(Uuid::INVALID_HYPHEN_PLACEMENT_ERROR) +- ->addViolation(); ++ ->setParameter( ++ '{{ value }}', ++ $this->formatValue($value) ++ ) ++ ->setCode(Uuid::INVALID_HYPHEN_PLACEMENT_ERROR) ++ ->addViolation(); + } else { + $this->buildViolation($constraint->message) +- ->setParameter( +- '{{ value }}', +- $this->formatValue($value) +- ) +- ->setCode(Uuid::INVALID_HYPHEN_PLACEMENT_ERROR) +- ->addViolation(); ++ ->setParameter( ++ '{{ value }}', ++ $this->formatValue($value) ++ ) ++ ->setCode(Uuid::INVALID_HYPHEN_PLACEMENT_ERROR) ++ ->addViolation(); + } + + return; + +--- vendor/symfony/validator/Constraints/Composite.php ++++ PHP_CodeSniffer +@@ -24,7 +24,7 @@ + * let {@link getCompositeOption()} return the name of the property which + * contains the nested constraints. + * +- * @since 2.6 ++ * @since 2.6 + * + * @author Bernhard Schussek + */ +@@ -97,13 +97,15 @@ + $excessGroups = array_diff($constraint->groups, $this->groups); + + if (count($excessGroups) > 0) { +- throw new ConstraintDefinitionException(sprintf( +- 'The group(s) "%s" passed to the constraint %s '. +- 'should also be passed to its containing constraint %s', +- implode('", "', $excessGroups), +- get_class($constraint), +- get_class($this) +- )); ++ throw new ConstraintDefinitionException( ++ sprintf( ++ 'The group(s) "%s" passed to the constraint %s '. ++ 'should also be passed to its containing constraint %s', ++ implode('", "', $excessGroups), ++ get_class($constraint), ++ get_class($this) ++ ) ++ ); + } + } else { + $constraint->groups = $this->groups; +@@ -124,7 +126,9 @@ + { + parent::addImplicitGroupName($group); + +- /** @var Constraint[] $nestedConstraints */ ++ /** ++ * @var Constraint[] $nestedConstraints ++*/ + $nestedConstraints = $this->{$this->getCompositeOption()}; + + foreach ($nestedConstraints as $constraint) { + +--- vendor/symfony/validator/Constraint.php ++++ PHP_CodeSniffer +@@ -78,11 +78,13 @@ + public static function getErrorName($errorCode) + { + if (!isset(static::$errorNames[$errorCode])) { +- throw new InvalidArgumentException(sprintf( +- 'The error code "%s" does not exist for constraint of type "%s".', +- $errorCode, +- get_called_class() +- )); ++ throw new InvalidArgumentException( ++ sprintf( ++ 'The error code "%s" does not exist for constraint of type "%s".', ++ $errorCode, ++ get_called_class() ++ ) ++ ); + } + + return static::$errorNames[$errorCode]; + +--- vendor/symfony/validator/Mapping/ClassMetadataInterface.php ++++ PHP_CodeSniffer +@@ -24,7 +24,7 @@ + * by a group sequence for that class and whether instances of that class + * should be traversed or not. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + * + +--- vendor/symfony/validator/Mapping/TraversalStrategy.php ++++ PHP_CodeSniffer +@@ -23,7 +23,7 @@ + * + * The traversal strategy is ignored for arrays. Arrays are always iterated. + * +- * @since 2.1 ++ * @since 2.1 + * + * @author Bernhard Schussek + * + +--- vendor/symfony/validator/Mapping/PropertyMetadataInterface.php ++++ PHP_CodeSniffer +@@ -24,7 +24,7 @@ + * should be validated against their class' metadata and whether traversable + * objects should be traversed or not. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + * + +--- vendor/symfony/validator/Mapping/GenericMetadata.php ++++ PHP_CodeSniffer +@@ -23,7 +23,7 @@ + * + * This class supports serialization and cloning. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + */ +@@ -131,11 +131,13 @@ + public function addConstraint(Constraint $constraint) + { + if ($constraint instanceof Traverse) { +- throw new ConstraintDefinitionException(sprintf( +- 'The constraint "%s" can only be put on classes. Please use '. +- '"Symfony\Component\Validator\Constraints\Valid" instead.', +- get_class($constraint) +- )); ++ throw new ConstraintDefinitionException( ++ sprintf( ++ 'The constraint "%s" can only be put on classes. Please use '. ++ '"Symfony\Component\Validator\Constraints\Valid" instead.', ++ get_class($constraint) ++ ) ++ ); + } + + if ($constraint instanceof Valid) { + +--- vendor/symfony/validator/Mapping/MemberMetadata.php ++++ PHP_CodeSniffer +@@ -97,10 +97,12 @@ + public function addConstraint(Constraint $constraint) + { + if (!in_array(Constraint::PROPERTY_CONSTRAINT, (array) $constraint->getTargets())) { +- throw new ConstraintDefinitionException(sprintf( +- 'The constraint %s cannot be put on properties or getters', +- get_class($constraint) +- )); ++ throw new ConstraintDefinitionException( ++ sprintf( ++ 'The constraint %s cannot be put on properties or getters', ++ get_class($constraint) ++ ) ++ ); + } + + parent::addConstraint($constraint); +@@ -113,11 +115,13 @@ + */ + public function __sleep() + { +- return array_merge(parent::__sleep(), array( ++ return array_merge( ++ parent::__sleep(), array( + 'class', + 'name', + 'property', +- )); ++ ) ++ ); + } + + /** + +--- vendor/symfony/validator/Mapping/Loader/StaticMethodLoader.php ++++ PHP_CodeSniffer +@@ -43,7 +43,9 @@ + */ + public function loadClassMetadata(ClassMetadata $metadata) + { +- /** @var \ReflectionClass $reflClass */ ++ /** ++ * @var \ReflectionClass $reflClass ++*/ + $reflClass = $metadata->getReflectionClass(); + + if (!$reflClass->isInterface() && $reflClass->hasMethod($this->methodName)) { + +--- vendor/symfony/validator/Mapping/MetadataInterface.php ++++ PHP_CodeSniffer +@@ -24,7 +24,7 @@ + * against their class' metadata and whether traversable objects should be + * traversed or not. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + * + +--- vendor/symfony/validator/Mapping/ClassMetadata.php ++++ PHP_CodeSniffer +@@ -136,7 +136,8 @@ + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.5 and will be removed in 3.0.', E_USER_DEPRECATED); + + if (null === $propagatedGroup && Constraint::DEFAULT_GROUP === $group +- && ($this->hasGroupSequence() || $this->isGroupSequenceProvider())) { ++ && ($this->hasGroupSequence() || $this->isGroupSequenceProvider()) ++ ) { + if ($this->hasGroupSequence()) { + $groups = $this->getGroupSequence()->groups; + } else { +@@ -177,7 +178,8 @@ + // Don't store the cascading strategy. Classes never cascade. + unset($parentProperties[array_search('cascadingStrategy', $parentProperties)]); + +- return array_merge($parentProperties, array( ++ return array_merge( ++ $parentProperties, array( + 'getters', + 'groupSequence', + 'groupSequenceProvider', +@@ -185,7 +187,8 @@ + 'name', + 'properties', + 'defaultGroup', +- )); ++ ) ++ ); + } + + /** +@@ -222,17 +225,21 @@ + public function addConstraint(Constraint $constraint) + { + if (!in_array(Constraint::CLASS_CONSTRAINT, (array) $constraint->getTargets())) { +- throw new ConstraintDefinitionException(sprintf( +- 'The constraint "%s" cannot be put on classes.', +- get_class($constraint) +- )); ++ throw new ConstraintDefinitionException( ++ sprintf( ++ 'The constraint "%s" cannot be put on classes.', ++ get_class($constraint) ++ ) ++ ); + } + + if ($constraint instanceof Valid) { +- throw new ConstraintDefinitionException(sprintf( +- 'The constraint "%s" cannot be put on classes.', +- get_class($constraint) +- )); ++ throw new ConstraintDefinitionException( ++ sprintf( ++ 'The constraint "%s" cannot be put on classes.', ++ get_class($constraint) ++ ) ++ ); + } + + if ($constraint instanceof Traverse) { + +--- vendor/symfony/validator/Mapping/CascadingStrategy.php ++++ PHP_CodeSniffer +@@ -27,7 +27,7 @@ + * Although the constants currently represent a boolean switch, they are + * implemented as bit mask in order to allow future extensions. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + * + +--- vendor/symfony/validator/Mapping/Factory/MetadataFactoryInterface.php ++++ PHP_CodeSniffer +@@ -16,7 +16,7 @@ + /** + * Returns {@link \Symfony\Component\Validator\Mapping\MetadataInterface} instances for values. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + */ + +--- vendor/symfony/validator/ValidatorBuilderInterface.php ++++ PHP_CodeSniffer +@@ -178,8 +178,8 @@ + * + * @return ValidatorBuilderInterface The builder object + * +- * @see Validation::API_VERSION_2_5 +- * @see Validation::API_VERSION_2_5_BC ++ * @see Validation::API_VERSION_2_5 ++ * @see Validation::API_VERSION_2_5_BC + * @deprecated since version 2.7, to be removed in 3.0. + */ + public function setApiVersion($apiVersion); + +--- vendor/symfony/validator/Util/PropertyPath.php ++++ PHP_CodeSniffer +@@ -16,7 +16,7 @@ + * + * For more extensive functionality, use Symfony's PropertyAccess component. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + */ + +--- vendor/symfony/validator/ExecutionContext.php ++++ PHP_CodeSniffer +@@ -104,17 +104,19 @@ + } + } + +- $this->globalContext->getViolations()->add(new ConstraintViolation( +- $translatedMessage, +- $message, +- $params, +- $this->globalContext->getRoot(), +- $this->propertyPath, +- // check using func_num_args() to allow passing null values +- func_num_args() >= 3 ? $invalidValue : $this->value, +- $plural, +- $code +- )); ++ $this->globalContext->getViolations()->add( ++ new ConstraintViolation( ++ $translatedMessage, ++ $message, ++ $params, ++ $this->globalContext->getRoot(), ++ $this->propertyPath, ++ // check using func_num_args() to allow passing null values ++ func_num_args() >= 3 ? $invalidValue : $this->value, ++ $plural, ++ $code ++ ) ++ ); + } + + /** +@@ -122,19 +124,21 @@ + */ + public function addViolationAt($subPath, $message, array $parameters = array(), $invalidValue = null, $plural = null, $code = null) + { +- $this->globalContext->getViolations()->add(new ConstraintViolation( +- null === $plural ++ $this->globalContext->getViolations()->add( ++ new ConstraintViolation( ++ null === $plural + ? $this->translator->trans($message, $parameters, $this->translationDomain) + : $this->translator->transChoice($message, $plural, $parameters, $this->translationDomain), +- $message, +- $parameters, +- $this->globalContext->getRoot(), +- $this->getPropertyPath($subPath), +- // check using func_num_args() to allow passing null values +- func_num_args() >= 4 ? $invalidValue : $this->value, +- $plural, +- $code +- )); ++ $message, ++ $parameters, ++ $this->globalContext->getRoot(), ++ $this->getPropertyPath($subPath), ++ // check using func_num_args() to allow passing null values ++ func_num_args() >= 4 ? $invalidValue : $this->value, ++ $plural, ++ $code ++ ) ++ ); + } + + /** + +--- vendor/symfony/validator/ConstraintViolationInterface.php ++++ PHP_CodeSniffer +@@ -59,7 +59,7 @@ + * @return array A possibly empty list of parameters indexed by the names + * that appear in the message template. + * +- * @see getMessageTemplate() ++ * @see getMessageTemplate() + * @deprecated since version 2.7, to be replaced by getParameters() in 3.0. + */ + public function getMessageParameters(); + +--- vendor/symfony/validator/Validator/LegacyValidator.php ++++ PHP_CodeSniffer +@@ -16,12 +16,12 @@ + /** + * A validator that supports both the API of Symfony < 2.5 and Symfony 2.5+. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + * +- * @see \Symfony\Component\Validator\ValidatorInterface +- * @see \Symfony\Component\Validator\Validator\ValidatorInterface ++ * @see \Symfony\Component\Validator\ValidatorInterface ++ * @see \Symfony\Component\Validator\Validator\ValidatorInterface + * @deprecated since version 2.5, to be removed in 3.0. + */ + class LegacyValidator extends RecursiveValidator + +--- vendor/symfony/validator/Validator/ContextualValidatorInterface.php ++++ PHP_CodeSniffer +@@ -17,7 +17,7 @@ + /** + * A validator in a specific execution context. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + */ + +--- vendor/symfony/validator/Validator/RecursiveContextualValidator.php ++++ PHP_CodeSniffer +@@ -33,7 +33,7 @@ + /** + * Recursive implementation of {@link ContextualValidatorInterface}. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + */ +@@ -177,11 +177,13 @@ + return $this; + } + +- throw new RuntimeException(sprintf( +- 'Cannot validate values of type "%s" automatically. Please '. +- 'provide a constraint.', +- gettype($value) +- )); ++ throw new RuntimeException( ++ sprintf( ++ 'Cannot validate values of type "%s" automatically. Please '. ++ 'provide a constraint.', ++ gettype($value) ++ ) ++ ); + } + + /** +@@ -194,12 +196,14 @@ + if (!$classMetadata instanceof ClassMetadataInterface) { + // Cannot be UnsupportedMetadataException because of BC with + // Symfony < 2.5 +- throw new ValidatorException(sprintf( +- 'The metadata factory should return instances of '. +- '"\Symfony\Component\Validator\Mapping\ClassMetadataInterface", '. +- 'got: "%s".', +- is_object($classMetadata) ? get_class($classMetadata) : gettype($classMetadata) +- )); ++ throw new ValidatorException( ++ sprintf( ++ 'The metadata factory should return instances of '. ++ '"\Symfony\Component\Validator\Mapping\ClassMetadataInterface", '. ++ 'got: "%s".', ++ is_object($classMetadata) ? get_class($classMetadata) : gettype($classMetadata) ++ ) ++ ); + } + + $propertyMetadatas = $classMetadata->getPropertyMetadata($propertyName); +@@ -245,12 +249,14 @@ + if (!$classMetadata instanceof ClassMetadataInterface) { + // Cannot be UnsupportedMetadataException because of BC with + // Symfony < 2.5 +- throw new ValidatorException(sprintf( +- 'The metadata factory should return instances of '. +- '"\Symfony\Component\Validator\Mapping\ClassMetadataInterface", '. +- 'got: "%s".', +- is_object($classMetadata) ? get_class($classMetadata) : gettype($classMetadata) +- )); ++ throw new ValidatorException( ++ sprintf( ++ 'The metadata factory should return instances of '. ++ '"\Symfony\Component\Validator\Mapping\ClassMetadataInterface", '. ++ 'got: "%s".', ++ is_object($classMetadata) ? get_class($classMetadata) : gettype($classMetadata) ++ ) ++ ); + } + + $propertyMetadatas = $classMetadata->getPropertyMetadata($propertyName); +@@ -345,12 +351,14 @@ + $classMetadata = $this->metadataFactory->getMetadataFor($object); + + if (!$classMetadata instanceof ClassMetadataInterface) { +- throw new UnsupportedMetadataException(sprintf( +- 'The metadata factory should return instances of '. +- '"Symfony\Component\Validator\Mapping\ClassMetadataInterface", '. +- 'got: "%s".', +- is_object($classMetadata) ? get_class($classMetadata) : gettype($classMetadata) +- )); ++ throw new UnsupportedMetadataException( ++ sprintf( ++ 'The metadata factory should return instances of '. ++ '"Symfony\Component\Validator\Mapping\ClassMetadataInterface", '. ++ 'got: "%s".', ++ is_object($classMetadata) ? get_class($classMetadata) : gettype($classMetadata) ++ ) ++ ); + } + + $this->validateClassNode( +@@ -552,15 +560,15 @@ + // group sequence and abort if necessary (G1, G2) + if ($group instanceof GroupSequence) { + $this->stepThroughGroupSequence( +- $object, +- $object, +- $cacheKey, +- $metadata, +- $propertyPath, +- $traversalStrategy, +- $group, +- $defaultOverridden ? Constraint::DEFAULT_GROUP : null, +- $context ++ $object, ++ $object, ++ $cacheKey, ++ $metadata, ++ $propertyPath, ++ $traversalStrategy, ++ $group, ++ $defaultOverridden ? Constraint::DEFAULT_GROUP : null, ++ $context + ); + + // Skip the group sequence when validating properties, because +@@ -586,12 +594,14 @@ + // returns two metadata objects, not just one + foreach ($metadata->getPropertyMetadata($propertyName) as $propertyMetadata) { + if (!$propertyMetadata instanceof PropertyMetadataInterface) { +- throw new UnsupportedMetadataException(sprintf( +- 'The property metadata instances should implement '. +- '"Symfony\Component\Validator\Mapping\PropertyMetadataInterface", '. +- 'got: "%s".', +- is_object($propertyMetadata) ? get_class($propertyMetadata) : gettype($propertyMetadata) +- )); ++ throw new UnsupportedMetadataException( ++ sprintf( ++ 'The property metadata instances should implement '. ++ '"Symfony\Component\Validator\Mapping\PropertyMetadataInterface", '. ++ 'got: "%s".', ++ is_object($propertyMetadata) ? get_class($propertyMetadata) : gettype($propertyMetadata) ++ ) ++ ); + } + + $propertyValue = $propertyMetadata->getPropertyValue($object); +@@ -632,11 +642,13 @@ + if (!$object instanceof \Traversable) { + // Must throw a ConstraintDefinitionException for backwards + // compatibility reasons with Symfony < 2.5 +- throw new ConstraintDefinitionException(sprintf( +- 'Traversal was enabled for "%s", but this class '. +- 'does not implement "\Traversable".', +- get_class($object) +- )); ++ throw new ConstraintDefinitionException( ++ sprintf( ++ 'Traversal was enabled for "%s", but this class '. ++ 'does not implement "\Traversable".', ++ get_class($object) ++ ) ++ ); + } + + $this->validateEachObjectIn( +@@ -690,15 +702,15 @@ + foreach ($groups as $key => $group) { + if ($group instanceof GroupSequence) { + $this->stepThroughGroupSequence( +- $value, +- $object, +- $cacheKey, +- $metadata, +- $propertyPath, +- $traversalStrategy, +- $group, +- null, +- $context ++ $value, ++ $object, ++ $cacheKey, ++ $metadata, ++ $propertyPath, ++ $traversalStrategy, ++ $group, ++ null, ++ $context + ); + + // Skip the group sequence when cascading, as the cascading +@@ -808,26 +820,26 @@ + + if ($metadata instanceof ClassMetadataInterface) { + $this->validateClassNode( +- $value, +- $cacheKey, +- $metadata, +- $propertyPath, +- $groups, +- $cascadedGroups, +- $traversalStrategy, +- $context ++ $value, ++ $cacheKey, ++ $metadata, ++ $propertyPath, ++ $groups, ++ $cascadedGroups, ++ $traversalStrategy, ++ $context + ); + } else { + $this->validateGenericNode( +- $value, +- $object, +- $cacheKey, +- $metadata, +- $propertyPath, +- $groups, +- $cascadedGroups, +- $traversalStrategy, +- $context ++ $value, ++ $object, ++ $cacheKey, ++ $metadata, ++ $propertyPath, ++ $groups, ++ $cascadedGroups, ++ $traversalStrategy, ++ $context + ); + } + + +--- vendor/symfony/validator/Validator/ValidatorInterface.php ++++ PHP_CodeSniffer +@@ -19,7 +19,7 @@ + /** + * Validates PHP values against constraints. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + */ + +--- vendor/symfony/validator/Validator/RecursiveValidator.php ++++ PHP_CodeSniffer +@@ -24,7 +24,7 @@ + /** + * Recursive implementation of {@link ValidatorInterface}. + * +- * @since 2.5 ++ * @since 2.5 + * + * @author Bernhard Schussek + */ + +--- vendor/symfony/stopwatch/Tests/StopwatchTest.php ++++ PHP_CodeSniffer +@@ -62,8 +62,7 @@ + + $stopwatchMockEvent = $this->getMockBuilder('Symfony\Component\Stopwatch\StopwatchEvent') + ->setConstructorArgs(array(microtime(true) * 1000)) +- ->getMock() +- ; ++ ->getMock(); + + $events->setValue(end($section), array('foo' => $stopwatchMockEvent)); + + +--- vendor/symfony/yaml/Dumper.php ++++ PHP_CodeSniffer +@@ -100,7 +100,8 @@ + + $willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value); + +- $output .= sprintf('%s%s%s%s', ++ $output .= sprintf( ++ '%s%s%s%s', + $prefix, + $isAHash ? Inline::dump($key, $flags).':' : '-', + $willBeInlined ? ' ' : "\n", + +--- vendor/symfony/yaml/Tests/ParserTest.php ++++ PHP_CodeSniffer +@@ -637,7 +637,8 @@ + */ + public function testMultipleDocumentsNotSupportedException() + { +- Yaml::parse(<<<'EOL' ++ Yaml::parse( ++ <<<'EOL' + # Ranking of 1998 home runs + --- + - Mark McGwire +@@ -657,7 +658,8 @@ + */ + public function testSequenceInAMapping() + { +- Yaml::parse(<<<'EOF' ++ Yaml::parse( ++ <<<'EOF' + yaml: + hash: me + - array stuff +@@ -717,7 +719,8 @@ + */ + public function testMappingInASequence() + { +- Yaml::parse(<<<'EOF' ++ Yaml::parse( ++ <<<'EOF' + yaml: + - array stuff + hash: me +@@ -731,7 +734,8 @@ + */ + public function testScalarInSequence() + { +- Yaml::parse(<<assertEquals(array( ++ $this->assertEquals( ++ array( + 'services' => array( + 'app.foo_service' => array( + 'class' => 'Foo', +@@ -802,7 +807,8 @@ + 'class' => 'Bar', + ), + ), +- ), Yaml::parse(<<<'EOF' ++ ), Yaml::parse( ++ <<<'EOF' + # comment 1 + services: + # comment 2 +@@ -814,12 +820,14 @@ + app/bar_service: + class: Bar + EOF +- )); ++ ) ++ ); + } + + public function testStringBlockWithComments() + { +- $this->assertEquals(array('content' => <<<'EOT' ++ $this->assertEquals( ++ array('content' => <<<'EOT' + # comment 1 + header + +@@ -830,7 +838,8 @@ + + footer # comment3 + EOT +- ), Yaml::parse(<<<'EOF' ++ ), Yaml::parse( ++ <<<'EOF' + content: | + # comment 1 + header +@@ -842,12 +851,14 @@ + + footer # comment3 + EOF +- )); ++ ) ++ ); + } + + public function testFoldedStringBlockWithComments() + { +- $this->assertEquals(array(array('content' => <<<'EOT' ++ $this->assertEquals( ++ array(array('content' => <<<'EOT' + # comment 1 + header + +@@ -858,7 +869,8 @@ + + footer # comment3 + EOT +- )), Yaml::parse(<<<'EOF' ++ )), Yaml::parse( ++ <<<'EOF' + - + content: | + # comment 1 +@@ -871,12 +883,14 @@ + + footer # comment3 + EOF +- )); ++ ) ++ ); + } + + public function testNestedFoldedStringBlockWithComments() + { +- $this->assertEquals(array(array( ++ $this->assertEquals( ++ array(array( + 'title' => 'some title', + 'content' => <<<'EOT' + # comment 1 +@@ -889,7 +903,8 @@ + + footer # comment3 + EOT +- )), Yaml::parse(<<<'EOF' ++ )), Yaml::parse( ++ <<<'EOF' + - + title: some title + content: | +@@ -903,12 +918,14 @@ + + footer # comment3 + EOF +- )); ++ ) ++ ); + } + + public function testReferenceResolvingInInlineStrings() + { +- $this->assertEquals(array( ++ $this->assertEquals( ++ array( + 'var' => 'var-value', + 'scalar' => 'var-value', + 'list' => array('var-value'), +@@ -918,7 +935,8 @@ + 'map' => array('key' => 'var-value'), + 'list_in_map' => array('key' => array('var-value')), + 'map_in_map' => array('foo' => array('bar' => 'var-value')), +- ), Yaml::parse(<<<'EOF' ++ ), Yaml::parse( ++ <<<'EOF' + var: &var var-value + scalar: *var + list: [ *var ] +@@ -929,7 +947,8 @@ + list_in_map: { key: [*var] } + map_in_map: { foo: { bar: *var } } + EOF +- )); ++ ) ++ ); + } + + public function testYamlDirective() +@@ -1188,13 +1207,13 @@ + data: !!binary | + SGVsbG8gd29ybGQ= + EOT +- ), ++ ), + 'containing spaces in block scalar' => array( + << + + + + ./tests/system + + + + + + + ./system + + ./system/ComposerScripts.php + ./system/ThirdParty + ./system/Debug/Toolbar/View + + + + diff --git a/codeigniter-3.0/user_guide/_static/ci-icon.ico b/ci-4.0-dev/public/favicon.ico similarity index 100% rename from codeigniter-3.0/user_guide/_static/ci-icon.ico rename to ci-4.0-dev/public/favicon.ico diff --git a/ci-4.0-dev/public/index.php b/ci-4.0-dev/public/index.php new file mode 100644 index 000000000..3584df7a2 --- /dev/null +++ b/ci-4.0-dev/public/index.php @@ -0,0 +1,281 @@ +load(); +unset($env); + +/* + * ------------------------------------------------------ + * Load the framework constants + * ------------------------------------------------------ + */ +if (file_exists(APPPATH.'Config/'.ENVIRONMENT.'/Constants.php')) +{ + require_once APPPATH.'Config/'.ENVIRONMENT.'/Constants.php'; +} + +require_once(APPPATH.'Config/Constants.php'); + +/* + * ------------------------------------------------------ + * Setup the autoloader + * ------------------------------------------------------ + */ +// The autoloader isn't initialized yet, so load the file manually. +require BASEPATH.'Autoloader/Autoloader.php'; +require APPPATH.'Config/Autoload.php'; +require APPPATH.'Config/Services.php'; + +// Use Config\Services as CodeIgniter\Services +class_alias('Config\Services', 'CodeIgniter\Services'); + +// The Autoloader class only handles namespaces +// and "legacy" support. +$loader = CodeIgniter\Services::autoloader(); +$loader->initialize(new Config\Autoload()); + +// The register function will prepend +// the psr4 loader. +$loader->register(); + +/* + * ------------------------------------------------------ + * Load the global functions + * ------------------------------------------------------ + */ + +require_once BASEPATH.'Common.php'; + +/* + * ------------------------------------------------------ + * Set custom exception handling + * ------------------------------------------------------ + */ +$config = new \Config\App(); + +CodeIgniter\Services::exceptions($config, true) + ->initialize(); + +//-------------------------------------------------------------------- +// Should we use a Composer autoloader? +//-------------------------------------------------------------------- + +if ($composer_autoload = $config->composerAutoload) +{ + if ($composer_autoload === TRUE) + { + file_exists(APPPATH.'../vendor/autoload.php') + ? require_once(APPPATH.'../vendor/autoload.php') + : log_message('error', '$config->\'composerAutoload\' is set to TRUE but '.realpath("../").'vendor/autoload.php was not found.'); + } + elseif (file_exists($composer_autoload)) + { + require_once($composer_autoload); + } + else + { + log_message('error', 'Could not find the specified $config->\'composerAutoload\' path: '.$composer_autoload); + } +} + +/* + * -------------------------------------------------------------------- + * LOAD THE BOOTSTRAP FILE + * -------------------------------------------------------------------- + * + * And away we go... + */ +$codeigniter = new CodeIgniter\CodeIgniter($startMemory, $startTime, $config); +$codeigniter->run(); + +require $_SERVER['DOCUMENT_ROOT'].'/php-framework-benchmark/libs/output_data.php'; diff --git a/ci-4.0-dev/public/robots.txt b/ci-4.0-dev/public/robots.txt new file mode 100644 index 000000000..9e60f970f --- /dev/null +++ b/ci-4.0-dev/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/ci-4.0-dev/rewrite.php b/ci-4.0-dev/rewrite.php new file mode 100644 index 000000000..f0bee90f7 --- /dev/null +++ b/ci-4.0-dev/rewrite.php @@ -0,0 +1,29 @@ + 'No error has occurred', + JSON_ERROR_DEPTH => 'The maximum stack depth has been exceeded', + JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON', + JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded', + JSON_ERROR_SYNTAX => 'Syntax error', + JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded', + ]; + + //-------------------------------------------------------------------- + + /** + * Takes the given data and formats it. + * + * @param $data + * + * @return mixed + */ + public function format(array $data) + { + $options = ENVIRONMENT == 'production' + ? JSON_NUMERIC_CHECK + : JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT; + + $result = json_encode($data, 512, $options); + + // If result is NULL, then an error happened. + // Let them know. + if ($result === null) + { + throw new \RuntimeException($this->errors[json_last_error()]); + } + + return utf8_encode($result); + } + + //-------------------------------------------------------------------- +} diff --git a/ci-4.0-dev/system/API/ResponseTrait.php b/ci-4.0-dev/system/API/ResponseTrait.php new file mode 100644 index 000000000..086b786ef --- /dev/null +++ b/ci-4.0-dev/system/API/ResponseTrait.php @@ -0,0 +1,319 @@ + 201, + 'deleted' => 200, + 'invalid_request' => 400, + 'unsupported_response_type' => 400, + 'invalid_scope' => 400, + 'temporarily_unavailable' => 400, + 'invalid_grant' => 400, + 'invalid_credentials' => 400, + 'invalid_refresh' => 400, + 'no_data' => 400, + 'invalid_data' => 400, + 'access_denied' => 401, + 'unauthorized' => 401, + 'invalid_client' => 401, + 'forbidden' => 403, + 'resource_not_found' => 404, + 'not_acceptable' => 406, + 'resource_exists' => 409, + 'conflict' => 409, + 'resource_gone' => 410, + 'payload_too_large' => 413, + 'unsupported_media_type' => 415, + 'too_many_requests' => 429, + 'server_error' => 500, + 'unsupported_grant_type' => 501, + 'not_implemented' => 501, + ]; + + //-------------------------------------------------------------------- + + /** + * Provides a single, simple method to return an API response, formatted + * to match the requested format, with proper content-type and status code. + * + * @param null $data + * @param int $status + * @param string $message + * + * @return mixed + */ + public function respond($data = null, int $status = null, string $message = '') + { + // If data is null and status code not provided, exit and bail + if ($data === null && $status === null) + { + $status = 404; + + // Create the output var here in case of $this->response([]); + $output = null; + } // If data is null but status provided, keep the output empty. + elseif ($data === null && is_numeric($status)) + { + $output = null; + } else + { + $status = empty($status) ? 200 : $status; + $output = $this->format($data); + } + + return $this->response->setBody($output) + ->setStatusCode($status, $message); + } + + //-------------------------------------------------------------------- + + /** + * Used for generic failures that no custom methods exist for. + * + * @param $messages + * @param int|null $status HTTP status code + * @param string|null $code Custom, API-specific, error code + * @param string $customMessage + * + * @return mixed + */ + public function fail($messages, int $status = 400, string $code = null, string $customMessage = '') + { + if (! is_array($messages)) + { + $messages = [$messages]; + } + + $response = [ + 'status' => $status, + 'error' => $code === null ? $status : $code, + 'messages' => $messages, + ]; + + return $this->respond($response, $status, $customMessage); + } + + //-------------------------------------------------------------------- + + //-------------------------------------------------------------------- + // Response Helpers + //-------------------------------------------------------------------- + + /** + * Used after successfully creating a new resource. + * + * @param $data + * @param string $message + * + * @return mixed + */ + public function respondCreated($data, string $message = '') + { + return $this->respond($data, $this->codes['created'], $message); + } + + //-------------------------------------------------------------------- + + /** + * Used after a resource has been successfully deleted. + * + * @param $data + * @param string $message + * + * @return mixed + */ + public function respondDeleted($data, string $message = '') + { + return $this->respond($data, $this->codes['deleted'], $message); + } + + //-------------------------------------------------------------------- + + /** + * Used when the client is either didn't send authorization information, + * or had bad authorization credentials. User is encouraged to try again + * with the proper information. + * + * @param string $description + * @param string $message + * + * @return mixed + */ + public function failUnauthorized(string $description, string $code=null, string $message = '') + { + return $this->fail($description, $this->codes['unauthorized'], $code, $message); + } + + //-------------------------------------------------------------------- + + /** + * Used when access is always denied to this resource and no amount + * of trying again will help. + * + * @param string $description + * @param string $message + * + * @return mixed + */ + public function failForbidden(string $description, string $code=null, string $message = '') + { + return $this->fail($description, $this->codes['forbidden'], $code, $message); + } + + //-------------------------------------------------------------------- + + /** + * Used when a specified resource cannot be found. + * + * @param string $description + * @param string $message + * + * @return mixed + */ + public function failNotFound(string $description, string $code=null, string $message = '') + { + return $this->fail($description, $this->codes['resource_not_found'], $code, $message); + } + + //-------------------------------------------------------------------- + + /** + * Used when the data provided by the client cannot be validated. + * + * @param string $description + * @param string $message + * + * @return mixed + */ + public function failValidationError(string $description, string $code=null, string $message = '') + { + return $this->fail($description, $this->codes['invalid_data'], $code, $message); + } + + //-------------------------------------------------------------------- + + /** + * Use when trying to create a new resource and it already exists. + * + * @param string $description + * @param string $message + * + * @return mixed + */ + public function failResourceExists(string $description, string $code=null, string $message = '') + { + return $this->fail($description, $this->codes['resource_exists'], $code, $message); + } + + //-------------------------------------------------------------------- + + /** + * Use when a resource was previously deleted. This is different than + * Not Found, because here we know the data previously existed, but is now gone, + * where Not Found means we simply cannot find any information about it. + * + * @param string $description + * @param string $message + * + * @return mixed + */ + public function failResourceGone(string $description, string $code=null, string $message = '') + { + return $this->fail($description, $this->codes['resource_gone'], $code, $message); + } + + //-------------------------------------------------------------------- + + /** + * Used when the user has made too many requests for the resource recently. + * + * @param string $description + * @param string $message + * + * @return mixed + */ + public function failTooManyRequests(string $description, string $code=null, string $message = '') + { + return $this->fail($description, $this->codes['too_many_requests'], $code, $message); + } + + //-------------------------------------------------------------------- + + + //-------------------------------------------------------------------- + // Utility Methods + //-------------------------------------------------------------------- + + /** + * Handles formatting a response. Currently makes some heavy assumptions + * and needs updating! :) + * + * @param null $data + * + * @return null|string + */ + protected function format($data = null) + { + // If the data is a string, there's not much we can do to it... + if (is_string($data)) + { + $this->setContentType('text/html'); + + return $data; + } + + $config = new \Config\API(); + + // Determine correct response type through content negotiation + $format = $this->request->negotiate('media', $config->supportedResponseFormats); + + $this->setContentType($format); + + $formatter = $config->getFormatter($format); + + return $formatter->format($data); + } + + //-------------------------------------------------------------------- + + /** + * Sets the response's content type. If a type is permitted + * ('html', 'json', or 'xml'), the appropriate content type is set. + * + * @param string $type + */ + protected function setContentType(string $type = null) + { + switch ($type) + { + case 'text/html': + $this->response = $this->response->setContentType('text/html'); + break; + case 'application/json': + $this->response = $this->response->setContentType('application/json'); + break; + case 'application/xml': + $this->response = $this->response->setContentType('text/xml'); + break; + } + } +} diff --git a/ci-4.0-dev/system/API/XMLFormatter.php b/ci-4.0-dev/system/API/XMLFormatter.php new file mode 100644 index 000000000..08af2efd0 --- /dev/null +++ b/ci-4.0-dev/system/API/XMLFormatter.php @@ -0,0 +1,65 @@ +"); + + $this->arrayToXML($data, $output); + + return $output->asXML(); + } + + //-------------------------------------------------------------------- + + /** + * A recursive method to convert an array into a valid XML string. + * + * Written by CodexWorld. Received permission by email on Nov 24, 2016 to use this code. + * + * @see http://www.codexworld.com/convert-array-to-xml-in-php/ + * + * @param array $data + * @param $output + */ + protected function arrayToXML(array $data, &$output) + { + foreach ($data as $key => $value) + { + if (is_array($value)) + { + if (! is_numeric($key)) + { + $subnode = $output->addChild("$key"); + $this->arrayToXML($value, $subnode); + } else + { + $subnode = $output->addChild("item{$key}"); + $this->arrayToXML($value, $subnode); + } + } else + { + $output->addChild("$key", htmlspecialchars("$value")); + } + } + } + + //-------------------------------------------------------------------- +} diff --git a/ci-4.0-dev/system/Autoloader/Autoloader.php b/ci-4.0-dev/system/Autoloader/Autoloader.php new file mode 100644 index 000000000..95cef3c57 --- /dev/null +++ b/ci-4.0-dev/system/Autoloader/Autoloader.php @@ -0,0 +1,370 @@ + [ + * 'Foo\Bar' => '/path/to/packages/foo-bar' + * ], + * 'classmap' => [ + * 'MyClass' => '/path/to/class/file.php' + * ] + * ]; + * + * Example: + * + * register(); + * + * @package CodeIgniter\Autoloader + */ +class Autoloader +{ + + /** + * Stores namespaces as key, and path as values. + * + * @var array + */ + protected $prefixes = []; + + /** + * Stores class name as key, and path as values. + * + * @var array + */ + protected $classmap = []; + + //-------------------------------------------------------------------- + + /** + * Reads in the configuration array (described above) and stores + * the valid parts that we'll need. + * + * @param $config + */ + public function initialize(\Config\Autoload $config) + { + // We have to have one or the other, though we don't enforce the need + // to have both present in order to work. + if (empty($config->psr4) && empty($config->classmap)) + { + throw new \InvalidArgumentException('Config array must contain either the \'psr4\' key or the \'classmap\' key.'); + } + + if (isset($config->psr4)) + { + $this->prefixes = $config->psr4; + } + + if (isset($config->classmap)) + { + $this->classmap = $config->classmap; + } + + unset($config); + } + + //-------------------------------------------------------------------- + + /** + * Register the loader with the SPL autoloader stack. + * + * @codeCoverageIgnore + */ + public function register() + { + // Since the default file extensions are searched + // in order of .inc then .php, but we always use .php, + // put the .php extension first to eek out a bit + // better performance. + // http://php.net/manual/en/function.spl-autoload.php#78053 + spl_autoload_extensions('.php,.inc'); + + // Prepend the PSR4 autoloader for maximum performance. + spl_autoload_register([$this, 'loadClass'], true, true); + + // Now prepend another loader for the files in our class map. + $config = is_array($this->classmap) ? $this->classmap : []; + + spl_autoload_register(function ($class) use ($config) + { + if ( ! array_key_exists($class, $config)) + { + return false; + } + + include_once $config[$class]; + }, + true, // Throw exception + true // Prepend + ); + } + + //-------------------------------------------------------------------- + + /** + * Registers a namespace with the autoloader. + * + * @param $namespace + * @param $path + * + * @return $this + */ + public function addNamespace($namespace, $path) + { + if (isset($this->prefixes[$namespace])) + { + if (is_string($this->prefixes[$namespace])) + { + $this->prefixes[$namespace] = [$this->prefixes[$namespace]]; + } + + $this->prefixes[$namespace] = array_merge($this->prefixes[$namespace], [$path]); + } + else + { + $this->prefixes[$namespace] = [$path]; + } + + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Removes a single namespace from the psr4 settings. + * + * @param $namespace + * + * @return $this + */ + public function removeNamespace($namespace) + { + unset($this->prefixes[$namespace]); + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Loads the class file for a given class name. + * + * @param string $class The fully qualified class name. + * + * @return mixed The mapped file on success, or boolean false + * on failure. + */ + public function loadClass($class) + { + $class = trim($class, '\\'); + $class = str_ireplace('.php', '', $class); + + $mapped_file = $this->loadInNamespace($class); + + // Nothing? One last chance by looking + // in common CodeIgniter folders. + if ( ! $mapped_file) + { + $mapped_file = $this->loadLegacy($class); + } + + return $mapped_file; + } + + //-------------------------------------------------------------------- + + /** + * Loads the class file for a given class name. + * + * @param string $class The fully-qualified class name + * + * @return mixed The mapped file name on success, or boolean false on fail + */ + protected function loadInNamespace($class) + { + if (strpos($class, '\\') === false) + { + return false; + } + + foreach ($this->prefixes as $namespace => $directories) + { + if (is_string($directories)) + { + $directories = [$directories]; + } + + foreach ($directories as $directory) + { + if (strpos($class, $namespace) === 0) { + $filePath = $directory . str_replace('\\', '/', substr($class, strlen($namespace))) . '.php'; + $filename = $this->requireFile($filePath); + + if ($filename) { + return $filename; + } + } + } + } + + // never found a mapped file + return false; + } + + //-------------------------------------------------------------------- + + /** + * Attempts to load the class from common locations in previous + * version of CodeIgniter, namely 'application/libraries', and + * 'application/Models'. + * + * @param $class The class name. This typically should NOT have a namespace. + * + * @return mixed The mapped file name on success, or boolean false on failure + */ + protected function loadLegacy($class) + { + // If there is a namespace on this class, then + // we cannot load it from traditional locations. + if (strpos('\\', $class) !== false) + { + return false; + } + + $paths = [ + APPPATH.'Controllers/', + APPPATH.'Libraries/', + APPPATH.'Models/', + ]; + + $class = str_replace('\\', '/', $class).'.php'; + + foreach ($paths as $path) + { + if ($file = $this->requireFile($path.$class)) + { + return $file; + } + } + + return false; + } + + //-------------------------------------------------------------------- + + /** + * A central way to require a file is loaded. Split out primarily + * for testing purposes. + * + * @codeCoverageIgnore + * + * @param $file + * + * @return bool + */ + protected function requireFile($file) + { + $file = $this->sanitizeFilename($file); + + if (file_exists($file)) + { + require_once $file; + + return $file; + } + + return false; + } + + //-------------------------------------------------------------------- + + /** + * Sanitizes a filename, replacing spaces with dashes. + * + * Removes special characters that are illegal in filenames on certain + * operating systems and special characters requiring special escaping + * to manipulate at the command line. Replaces spaces and consecutive + * dashes with a single dash. Trim period, dash and underscore from beginning + * and end of filename. + * + * @param string $filename + * + * @return string The sanitized filename + */ + public function sanitizeFilename(string $filename): string + { + // Only allow characters deemed safe for POSIX portable filenames. + // Plus the forward slash for directory separators since this might + // be a path. + // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_278 + // Modified to allow backslash and colons for on Windows machines. + $filename = preg_replace('/[^a-zA-Z0-9\s\/\-\_\.\:\\\\]/', '', $filename); + + // Clean up our filename edges. + $filename = trim($filename, '.-_'); + + return $filename; + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Autoloader/FileLocator.php b/ci-4.0-dev/system/Autoloader/FileLocator.php new file mode 100644 index 000000000..e7316d8c5 --- /dev/null +++ b/ci-4.0-dev/system/Autoloader/FileLocator.php @@ -0,0 +1,250 @@ +namespaces = $autoload->psr4; + + unset($autoload); + + // Always keep the Application directory as a "package". + array_unshift($this->namespaces, APPPATH); + } + + //-------------------------------------------------------------------- + + /** + * Attempts to locate a file by examining the name for a namespace + * and looking through the PSR-4 namespaced files that we know about. + * + * @param string $file The namespaced file to locate + * @param string $folder The folder within the namespace that we should look for the file. + * @param string $ext The file extension the file should have. + * + * @return string The path to the file if found, or an empty string. + */ + public function locateFile(string $file, string $folder=null, string $ext = 'php'): string + { + // Ensure the extension is on the filename + $file = strpos($file, '.'.$ext) !== false + ? $file + : $file.'.'.$ext; + + // Clean the folder name from the filename + if (! empty($folder)) + { + $file = str_replace($folder.'/', '', $file); + } + + // No namespaceing? Try the application folder. + if (strpos($file, '\\') === false) + { + return $this->legacyLocate($file, $folder); + } + + // Standardize slashes to handle nested directories. + $file = str_replace('/', '\\', $file); + + $segments = explode('\\', $file); + + // The first segment will be empty if a slash started the filename. + if (empty($segments[0])) unset($segments[0]); + + $path = ''; + $prefix = ''; + $filename = ''; + + while (! empty($segments)) + { + $prefix .= empty($prefix) + ? ucfirst(array_shift($segments)) + : '\\'. ucfirst(array_shift($segments)); + + if (! array_key_exists($prefix, $this->namespaces)) + { + continue; + } + + $path = $this->namespaces[$prefix].'/'; + $filename = implode('/', $segments); + break; + } + + // IF we have a folder name, then the calling function + // expects this file to be within that folder, like 'Views', + // or 'libraries'. + // @todo Allow it to check with and without the nested folder. + if (! empty($folder) && strpos($filename, $folder) === false) + { + $filename = $folder.'/'.$filename; + } + + $path .= $filename; + + if (! $this->requireFile($path)) + { + $path = ''; + } + + return $path; + } + + //-------------------------------------------------------------------- + + /** + * Searches through all of the defined namespaces looking for a file. + * Returns an array of all found locations for the defined file. + * + * Example: + * + * $locator->search('Config/Routes.php'); + * // Assuming PSR4 namespaces include foo and bar, might return: + * [ + * 'application/modules/foo/Config/Routes.php', + * 'application/modules/bar/Config/Routes.php', + * ] + * + * @param string $path + * @param string $ext + * + * @return array + */ + public function search(string $path, string $ext = 'php'): array + { + $foundPaths = []; + + // Ensure the extension is on the filename + $path = strpos($path, '.'.$ext) !== false + ? $path + : $path.'.'.$ext; + + foreach ($this->namespaces as $name => $folder) + { + $folder = rtrim($folder, '/').'/'; + + if (file_exists($folder.$path)) + { + $foundPaths[] = $folder.$path; + } + } + + // Remove any duplicates + $foundPaths = array_unique($foundPaths); + + return $foundPaths; + } + + //-------------------------------------------------------------------- + + /** + * Checks the application folder to see if the file can be found. + * Only for use with filenames that DO NOT include namespacing. + * + * @param string $file + * @param string|null $folder + * + * @return string + * @internal param string $ext + * + */ + protected function legacyLocate(string $file, string $folder=null): string + { + $paths = [APPPATH, BASEPATH]; + + foreach ($paths as $path) + { + $path .= empty($folder) + ? $file + : $folder.'/'.$file; + + if ($this->requireFile($path) === true) + { + return $path; + } + } + + return ''; + } + + //-------------------------------------------------------------------- + + /** + * Checks to see if a file exists on the file system. This is split + * out to it's own method to make testing simpler. + * + * @codeCoverageIgnore + * @param string $path + * + * @return bool + */ + protected function requireFile(string $path): bool + { + return file_exists($path); + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/CLI/CLI.php b/ci-4.0-dev/system/CLI/CLI.php new file mode 100644 index 000000000..bd5a1612e --- /dev/null +++ b/ci-4.0-dev/system/CLI/CLI.php @@ -0,0 +1,613 @@ + '0;30', + 'dark_gray' => '1;30', + 'blue' => '0;34', + 'dark_blue' => '1;34', + 'light_blue' => '1;34', + 'green' => '0;32', + 'light_green' => '1;32', + 'cyan' => '0;36', + 'light_cyan' => '1;36', + 'red' => '0;31', + 'light_red' => '1;31', + 'purple' => '0;35', + 'light_purple' => '1;35', + 'light_yellow' => '0;33', + 'yellow' => '1;33', + 'light_gray' => '0;37', + 'white' => '1;37', + ]; + + /** + * Background color list + * @var array + */ + protected static $background_colors = [ + 'black' => '40', + 'red' => '41', + 'green' => '42', + 'yellow' => '43', + 'blue' => '44', + 'magenta' => '45', + 'cyan' => '46', + 'light_gray' => '47', + ]; + + //-------------------------------------------------------------------- + + /** + * Static "constructor". + */ + public static function init() + { + static::$readline_support = extension_loaded('readline'); + + static::$initialized = true; + } + + //-------------------------------------------------------------------- + + /** + * Get input from the shell, using readline or the standard STDIN + * + * Named options must be in the following formats: + * php index.php user -v --v -name=John --name=John + * + * @param string $prefix + * + * @return string + */ + public static function input(string $prefix = null): string + { + if (static::$readline_support) + { + return readline($prefix); + } + + echo $prefix; + + return fgets(STDIN); + } + + //-------------------------------------------------------------------- + + /** + * Asks the user for input. This can have either 1 or 2 arguments. + * + * Usage: + * + * // Waits for any key press + * CLI::prompt(); + * + * // Takes any input + * $color = CLI::prompt('What is your favorite color?'); + * + * // Takes any input, but offers default + * $color = CLI::prompt('What is your favourite color?', 'white'); + * + * // Will only accept the options in the array + * $ready = CLI::prompt('Are you ready?', array('y','n')); + * + * @return string the user input + */ + public static function prompt(): string + { + $args = func_get_args(); + + $options = []; + $output = ''; + $default = null; + + // How many we got + $arg_count = count($args); + + // Is the last argument a boolean? True means required + $required = end($args) === true; + + // Reduce the argument count if required was passed, we don't care about that anymore + $required === true && --$arg_count; + + // This method can take a few crazy combinations of arguments, so lets work it out + switch ($arg_count) + { + case 2: + + // E.g: $ready = CLI::prompt('Are you ready?', array('y','n')); + if (is_array($args[1])) + { + list($output, $options) = $args; + } + + // E.g: $color = CLI::prompt('What is your favourite color?', 'white'); + elseif (is_string($args[1])) + { + list($output, $default) = $args; + } + + break; + + case 1: + + // No question (probably been asked already) so just show options + // E.g: $ready = CLI::prompt(array('y','n')); + if (is_array($args[0])) + { + $options = $args[0]; + } + + // Question without options + // E.g: $ready = CLI::prompt('What did you do today?'); + elseif (is_string($args[0])) + { + $output = $args[0]; + } + + break; + } + + // If a question has been asked with the read + if ($output !== '') + { + $extra_output = ''; + + if ($default !== null) + { + $extra_output = ' [ Default: "'.$default.'" ]'; + } + + elseif ($options !== []) + { + $extra_output = ' [ '.implode(', ', $options).' ]'; + } + + fwrite(STDOUT, $output.$extra_output.': '); + } + + // Read the input from keyboard. + $input = trim(static::input()) ? : $default; + + // No input provided and we require one (default will stop this being called) + if (empty($input) && $required === true) + { + static::write('This is required.'); + static::newLine(); + + $input = forward_static_call_array([__CLASS__, 'prompt'], $args); + } + + // If options are provided and the choice is not in the array, tell them to try again + if ( ! empty($options) && ! in_array($input, $options)) + { + static::write('This is not a valid option. Please try again.'); + static::newLine(); + + $input = forward_static_call_array([__CLASS__, 'prompt'], $args); + } + + return empty($input) ? '' : $input; + } + + //-------------------------------------------------------------------- + + /** + * Outputs a string to the cli. + * + * @param string $text the text to output + * @param string $foreground + * @param string $background + */ + public static function write(string $text, string $foreground = null, string $background = null) + { + if ($foreground || $background) + { + $text = static::color($text, $foreground, $background); + } + + fwrite(STDOUT, $text.PHP_EOL); + } + + //-------------------------------------------------------------------- + + /** + * Outputs an error to the CLI using STDERR instead of STDOUT + * + * @param string|array $text the text to output, or array of errors + * @param string $foreground + * @param string $background + */ + public static function error(string $text, string $foreground = 'light_red', string $background = null) + { + if ($foreground || $background) + { + $text = static::color($text, $foreground, $background); + } + + fwrite(STDERR, $text.PHP_EOL); + } + + //-------------------------------------------------------------------- + + /** + * Beeps a certain number of times. + * + * @param int $num the number of times to beep + */ + public static function beep(int $num = 1) + { + echo str_repeat("\x07", $num); + } + + //-------------------------------------------------------------------- + + /** + * Waits a certain number of seconds, optionally showing a wait message and + * waiting for a key press. + * + * @param int $seconds number of seconds + * @param bool $countdown show a countdown or not + */ + public static function wait(int $seconds, bool $countdown = false) + { + if ($countdown === true) + { + $time = $seconds; + + while ($time > 0) + { + fwrite(STDOUT, $time.'... '); + sleep(1); + $time--; + } + static::write(); + } + + else + { + if ($seconds > 0) + { + sleep($seconds); + } + else + { + static::write(static::$wait_msg); + static::input(); + } + } + } + + + //-------------------------------------------------------------------- + + /** + * if operating system === windows + */ + public static function isWindows() + { + return 'win' === strtolower(substr(php_uname("s"), 0, 3)); + } + + //-------------------------------------------------------------------- + + /** + * Enter a number of empty lines + * + * @param int $num Number of lines to output + * + * @return void + */ + public static function newLine(int $num = 1) + { + // Do it once or more, write with empty string gives us a new line + for ($i = 0; $i < $num; $i++) + { + static::write(); + } + } + + //-------------------------------------------------------------------- + + /** + * Clears the screen of output + * + * @return void + */ + public static function clearScreen() + { + static::isWindows() + + // Windows is a bit crap at this, but their terminal is tiny so shove this in + ? static::newLine(40) + + // Anything with a flair of Unix will handle these magic characters + : fwrite(STDOUT, chr(27)."[H".chr(27)."[2J"); + } + + //-------------------------------------------------------------------- + + /** + * Returns the given text with the correct color codes for a foreground and + * optionally a background color. + * + * @param string $text the text to color + * @param string $foreground the foreground color + * @param string $background the background color + * @param string $format other formatting to apply. Currently only 'underline' is understood + * + * @return string the color coded string + */ + public static function color(string $text, string $foreground, string $background = null, string $format = null) + { + if (static::isWindows() && ! isset($_SERVER['ANSICON'])) + { + return $text; + } + + if ( ! array_key_exists($foreground, static::$foreground_colors)) + { + throw new \RuntimeException('Invalid CLI foreground color: '.$foreground); + } + + if ($background !== null && ! array_key_exists($background, static::$background_colors)) + { + throw new \RuntimeException('Invalid CLI background color: '.$background); + } + + $string = "\033[".static::$foreground_colors[$foreground]."m"; + + if ($background !== null) + { + $string .= "\033[".static::$background_colors[$background]."m"; + } + + if ($format === 'underline') + { + $string .= "\033[4m"; + } + + $string .= $text."\033[0m"; + + return $string; + } + + //-------------------------------------------------------------------- + + /** + * Attempts to determine the width of the viewable CLI window. + * This only works on *nix-based systems, so return a sane default + * for Windows environments. + * + * @param int $default + * + * @return int + */ + public static function getWidth(int $default = 80): int + { + if (static::isWindows()) + { + return $default; + } + + return (int)shell_exec('tput cols'); + } + + //-------------------------------------------------------------------- + + /** + * Attempts to determine the height of the viewable CLI window. + * This only works on *nix-based systems, so return a sane default + * for Windows environments. + * + * @param int $default + * + * @return int + */ + public static function getHeight(int $default = 32): int + { + if (static::isWindows()) + { + return $default; + } + + return (int)shell_exec('tput lines'); + } + + //-------------------------------------------------------------------- + + /** + * Displays a progress bar on the CLI. You must call it repeatedly + * to update it. Set $thisStep = false to erase the progress bar. + * + * @param int $thisStep + * @param int $totalSteps + */ + public static function showProgress($thisStep = 1, int $totalSteps = 10) + { + // The first time through, save + // our position so the script knows where to go + // back to when writing the bar, and + // at the end of the script. + if ( ! static::$inProgress) + { + fwrite(STDOUT, "\0337"); + static::$inProgress = true; + } + + // Restore position + fwrite(STDERR, "\0338"); + + if ($thisStep !== false) + { + // Don't allow div by zero or negative numbers.... + $thisStep = abs($thisStep); + $totalSteps = $totalSteps < 1 ? 1 : $totalSteps; + + $percent = intval(($thisStep / $totalSteps) * 100); + $step = (int)round($percent / 10); + + // Write the progress bar + fwrite(STDOUT, "[\033[32m".str_repeat('#', $step).str_repeat('.', 10 - $step)."\033[0m]"); + // Textual representation... + fwrite(STDOUT, " {$percent}% Complete".PHP_EOL); + // Move up, undo the PHP_EOL + fwrite(STDOUT, "\033[1A"); + } + else + { + fwrite(STDERR, "\007"); + } + } + + //-------------------------------------------------------------------- + + /** + * Takes a string and writes it to the command line, wrapping to a maximum + * width. If no maximum width is specified, will wrap to the window's max + * width. + * + * If an int is passed into $pad_left, then all strings after the first + * will padded with that many spaces to the left. Useful when printing + * short descriptions that need to start on an existing line. + * + * @param string $string + * @param int $max + * @param int $pad_left + * + * @return string + */ + public static function wrap(string $string = null, int $max = 0, int $pad_left = 0): string + { + if (empty($string)) + { + return ''; + } + + if ($max == 0) + { + $max = CLI::getWidth(); + } + + if (CLI::getWidth() < $max) + { + $max = CLI::getWidth(); + } + + $max = $max - $pad_left; + + $lines = wordwrap($string, $max); + + if ($pad_left > 0) + { + $lines = explode("\n", $lines); + + $first = true; + + array_walk($lines, function (&$line, $index) use ($max, $pad_left, &$first) + { + if ( ! $first) + { + $line = str_repeat(" ", $pad_left).$line; + } + else + { + $first = false; + } + }); + + $lines = implode("\n", $lines); + } + + return $lines; + } + + //-------------------------------------------------------------------- + +} + +// Ensure the class is initialized. +CLI::init(); diff --git a/ci-4.0-dev/system/Cache/CacheFactory.php b/ci-4.0-dev/system/Cache/CacheFactory.php new file mode 100644 index 000000000..4900c6e61 --- /dev/null +++ b/ci-4.0-dev/system/Cache/CacheFactory.php @@ -0,0 +1,64 @@ +validHandlers) || ! is_array($config->validHandlers)) + { + throw new \InvalidArgumentException(lang('Cache.cacheInvalidHandlers')); + } + + if (! isset($config->handler) || ! isset($config->backupHandler)) + { + throw new \InvalidArgumentException(lang('Cache.cacheNoBackup')); + } + + $handler = ! empty($handler) ? $handler : $config->handler; + $backup = ! empty($backup) ? $backup : $config->backupHandler; + + if (! array_key_exists($handler, $config->validHandlers) || ! array_key_exists($backup, $config->validHandlers)) + { + throw new \InvalidArgumentException(lang('Cache.cacheHandlerNotFound')); + } + + // Get an instance of our handler. + $adapter = new $config->validHandlers[$handler]($config); + + if (! $adapter->isSupported()) + { + $adapter = new $config->validHandlers[$backup]($config); + + if (! $adapter->isSupported()) + { + // Log stuff here, don't throw exception. No need to raise a fuss. + + // Fall back to the dummy adapter. + $adapter = new $config->validHandler['dummy'](); + } + } + + $adapter->initialize(); + + return $adapter; + } + + //-------------------------------------------------------------------- + +} \ No newline at end of file diff --git a/ci-4.0-dev/system/Cache/CacheInterface.php b/ci-4.0-dev/system/Cache/CacheInterface.php new file mode 100644 index 000000000..4a68d273f --- /dev/null +++ b/ci-4.0-dev/system/Cache/CacheInterface.php @@ -0,0 +1,115 @@ +prefix = $config->prefix ?: ''; + $this->path = ! empty($config->path) + ? $config->path + : WRITEPATH.'cache'; + + $this->path = rtrim($this->path, '/').'/'; + } + + //-------------------------------------------------------------------- + + /** + * Takes care of any handler-specific setup that must be done. + */ + public function initialize() + { + // Not to see here... + } + + //-------------------------------------------------------------------- + + /** + * Attempts to fetch an item from the cache store. + * + * @param string $key Cache item name + * + * @return mixed + */ + public function get(string $key) + { + $key = $this->prefix.$key; + + $data = $this->getItem($key); + + return is_array($data) ? $data['data'] : false; + } + + //-------------------------------------------------------------------- + + /** + * Saves an item to the cache store. + * + * The $raw parameter is only utilized by Mamcache in order to + * allow usage of increment() and decrement(). + * + * @param string $key Cache item name + * @param $value the data to save + * @param null $ttl Time To Live, in seconds (default 60) + * @param bool $raw Whether to store the raw value. + * + * @return mixed + */ + public function save(string $key, $value, int $ttl = 60, bool $raw = false) + { + $key = $this->prefix.$key; + + $contents = [ + 'time' => time(), + 'ttl' => $ttl, + 'data' => $value, + ]; + + if ($this->writeFile($this->path.$key, serialize($contents))) + { + chmod($this->path.$key, 0640); + + return true; + } + + return false; + } + + //-------------------------------------------------------------------- + + /** + * Deletes a specific item from the cache store. + * + * @param string $key Cache item name + * + * @return mixed + */ + public function delete(string $key) + { + $key = $this->prefix.$key; + + return file_exists($this->path.$key) + ? unlink($this->path.$key) + : false; + } + + //-------------------------------------------------------------------- + + /** + * Performs atomic incrementation of a raw stored value. + * + * @param string $key Cache ID + * @param int $offset Step/value to increase by + * + * @return mixed + */ + public function increment(string $key, int $offset = 1) + { + $key = $this->prefix.$key; + + $data = $this->getItem($key); + + if ($data === false) + { + $data = ['data' => 0, 'ttl' => 60]; + } + elseif (! is_int($data['data'])) + { + return false; + } + + $new_value = $data['data']+$offset; + + return $this->save($key, $new_value, $data['ttl']) + ? $new_value + : false; + } + + //-------------------------------------------------------------------- + + /** + * Performs atomic decrementation of a raw stored value. + * + * @param string $key Cache ID + * @param int $offset Step/value to increase by + * + * @return mixed + */ + public function decrement(string $key, int $offset = 1) + { + $key = $this->prefix.$key; + + $data = $this->getItem($key); + + if ($data === false) + { + $data = ['data' => 0, 'ttl' => 60]; + } + elseif (! is_int($data['data'])) + { + return false; + } + + $new_value = $data['data']-$offset; + + return $this->save($key, $new_value, $data['ttl']) + ? $new_value + : false; + } + + //-------------------------------------------------------------------- + + /** + * Will delete all items in the entire cache. + * + * @return mixed + */ + public function clean() + { + return $this->deleteFiles($this->path, false, true); + } + + //-------------------------------------------------------------------- + + /** + * Returns information on the entire cache. + * + * The information returned and the structure of the data + * varies depending on the handler. + * + * @return mixed + */ + public function getCacheInfo() + { + return $this->getDirFileInfo($this->path); + } + + //-------------------------------------------------------------------- + + /** + * Returns detailed information about the specific item in the cache. + * + * @param string $key Cache item name. + * + * @return mixed + */ + public function getMetaData(string $key) + { + $key = $this->prefix.$key; + + if ( ! file_exists($this->path.$key)) + { + return FALSE; + } + + $data = unserialize(file_get_contents($this->path.$key)); + + if (is_array($data)) + { + $mtime = filemtime($this->path.$key); + + if ( ! isset($data['ttl'])) + { + return FALSE; + } + + return array( + 'expire' => $mtime + $data['ttl'], + 'mtime' => $mtime + ); + } + + return FALSE; + } + + //-------------------------------------------------------------------- + + /** + * Determines if the driver is supported on this system. + * + * @return boolean + */ + public function isSupported(): bool + { + return is_writable($this->path); + } + + //-------------------------------------------------------------------- + + /** + * Does the heavy lifting of actually retrieving the file and + * verifying it's age. + * + * @param string $key + * + * @return bool|mixed + */ + protected function getItem(string $key) + { + if (! is_file($this->path.$key)) + { + return null; + } + + $data = unserialize(file_get_contents($this->path.$key)); + + if ($data['ttl'] > 0 && time() > $data['time']+$data['ttl']) + { + unlink($this->path.$key); + + return null; + } + + return $data; + } + + //-------------------------------------------------------------------- + + //-------------------------------------------------------------------- + // SUPPORT METHODS FOR FILES + //-------------------------------------------------------------------- + + /** + * Writes a file to disk, or returns false if not successful. + * + * @param $path + * @param $data + * @param string $mode + * + * @return bool + */ + protected function writeFile($path, $data, $mode = 'wb') + { + if (! $fp = @fopen($path, $mode)) + { + return false; + } + + flock($fp, LOCK_EX); + + for ($result = $written = 0, $length = strlen($data); $written < $length; $written += $result) + { + if (($result = fwrite($fp, substr($data, $written))) === false) + { + break; + } + } + + flock($fp, LOCK_UN); + fclose($fp); + + return is_int($result); + } + + //-------------------------------------------------------------------- + + /** + * Delete Files + * + * Deletes all files contained in the supplied directory path. + * Files must be writable or owned by the system in order to be deleted. + * If the second parameter is set to TRUE, any directories contained + * within the supplied base directory will be nuked as well. + * + * @param string $path File path + * @param bool $del_dir Whether to delete any directories found in the path + * @param bool $htdocs Whether to skip deleting .htaccess and index page files + * @param int $_level Current directory depth level (default: 0; internal use only) + * + * @return bool + */ + protected function deleteFiles($path, $del_dir = false, $htdocs = false, $_level = 0) + { + // Trim the trailing slash + $path = rtrim($path, '/\\'); + + if (! $current_dir = @opendir($path)) + { + return false; + } + + while (false !== ($filename = @readdir($current_dir))) + { + if ($filename !== '.' && $filename !== '..') + { + if (is_dir($path.DIRECTORY_SEPARATOR.$filename) && $filename[0] !== '.') + { + $this->deleteFiles($path.DIRECTORY_SEPARATOR.$filename, $del_dir, $htdocs, $_level+1); + } + elseif ($htdocs !== true || ! preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename)) + { + @unlink($path.DIRECTORY_SEPARATOR.$filename); + } + } + } + + closedir($current_dir); + + return ($del_dir === true && $_level > 0) + ? @rmdir($path) + : true; + } + + //-------------------------------------------------------------------- + + /** + * Get Directory File Information + * + * Reads the specified directory and builds an array containing the filenames, + * filesize, dates, and permissions + * + * Any sub-folders contained within the specified path are read as well. + * + * @param string path to source + * @param bool Look only at the top level directory specified? + * @param bool internal variable to determine recursion status - do not use in calls + * + * @return array + */ + protected function getDirFileInfo($source_dir, $top_level_only = true, $_recursion = false) + { + static $_filedata = []; + $relative_path = $source_dir; + + if ($fp = @opendir($source_dir)) + { + // reset the array and make sure $source_dir has a trailing slash on the initial call + if ($_recursion === false) + { + $_filedata = []; + $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; + } + + // Used to be foreach (scandir($source_dir, 1) as $file), but scandir() is simply not as fast + while (false !== ($file = readdir($fp))) + { + if (is_dir($source_dir.$file) && $file[0] !== '.' && $top_level_only === false) + { + $this->getDirFileInfo($source_dir.$file.DIRECTORY_SEPARATOR, $top_level_only, true); + } + elseif ($file[0] !== '.') + { + $_filedata[$file] = $this->getFileInfo($source_dir.$file); + $_filedata[$file]['relative_path'] = $relative_path; + } + } + + closedir($fp); + + return $_filedata; + } + + return false; + } + + //-------------------------------------------------------------------- + + /** + * Get File Info + * + * Given a file and path, returns the name, path, size, date modified + * Second parameter allows you to explicitly declare what information you want returned + * Options are: name, server_path, size, date, readable, writable, executable, fileperms + * Returns FALSE if the file cannot be found. + * + * @param string path to file + * @param mixed array or comma separated string of information returned + * + * @return array + */ + protected function getFileInfo($file, $returned_values = ['name', 'server_path', 'size', 'date']) + { + if (! file_exists($file)) + { + return false; + } + + if (is_string($returned_values)) + { + $returned_values = explode(',', $returned_values); + } + + foreach ($returned_values as $key) + { + switch ($key) + { + case 'name': + $fileinfo['name'] = basename($file); + break; + case 'server_path': + $fileinfo['server_path'] = $file; + break; + case 'size': + $fileinfo['size'] = filesize($file); + break; + case 'date': + $fileinfo['date'] = filemtime($file); + break; + case 'readable': + $fileinfo['readable'] = is_readable($file); + break; + case 'writable': + $fileinfo['writable'] = is_writable($file); + break; + case 'executable': + $fileinfo['executable'] = is_executable($file); + break; + case 'fileperms': + $fileinfo['fileperms'] = fileperms($file); + break; + } + } + + return $fileinfo; + } + + //-------------------------------------------------------------------- +} \ No newline at end of file diff --git a/ci-4.0-dev/system/Cache/Handlers/MemcachedHandler.php b/ci-4.0-dev/system/Cache/Handlers/MemcachedHandler.php new file mode 100644 index 000000000..fdb984386 --- /dev/null +++ b/ci-4.0-dev/system/Cache/Handlers/MemcachedHandler.php @@ -0,0 +1,303 @@ + [ + 'host' => '127.0.0.1', + 'port' => 11211, + 'weight' => 1, + ], + ]; + + //-------------------------------------------------------------------- + + public function __construct($config) + { + $this->prefix = $config->prefix ?: ''; + + if (isset($config->memcached)) + { + $this->config = $config->memcached; + } + } + + //-------------------------------------------------------------------- + + /** + * Takes care of any handler-specific setup that must be done. + */ + public function initialize() + { + $defaults = $this->config['default']; + + if (class_exists('Memcached')) + { + $this->memcached = new Memcached(); + } + elseif (class_exists('Memcache')) + { + $this->memcached = new Memcache(); + } + else + { +// log_message('error', 'Cache: Failed to create Memcache(d) object; extension not loaded?'); + + return; + } + + foreach ($this->config as $cacheName => $cacheServer) + { + if (! isset($cacheServer['hostname'])) + { +// log_message('debug', 'Cache: Memcache(d) configuration "'.$cacheName.'" doesn\'t include a hostname; ignoring.'); + continue; + } + elseif ($cacheServer['hostname'][0] === '/') + { + $cacheServer['port'] = 0; + } + elseif (empty($cacheServer['port'])) + { + $cacheServer['port'] = $defaults['port']; + } + + isset($cacheServer['weight']) OR $cacheServer['weight'] = $defaults['weight']; + + if ($this->memcached instanceof Memcache) + { + // Third parameter is persistance and defaults to TRUE. + $this->memcached->addServer( + $cacheServer['hostname'], + $cacheServer['port'], + true, + $cacheServer['weight'] + ); + } + elseif ($this->memcached instanceof Memcached) + { + $this->memcached->addServer( + $cacheServer['hostname'], + $cacheServer['port'], + $cacheServer['weight'] + ); + } + } + } + + //-------------------------------------------------------------------- + + + /** + * Attempts to fetch an item from the cache store. + * + * @param string $key Cache item name + * + * @return mixed + */ + public function get(string $key) + { + $key = $this->prefix.$key; + + $data = $this->memcached->get($key); + + return is_array($data) ? $data[0] : $data; + } + + //-------------------------------------------------------------------- + + /** + * Saves an item to the cache store. + * + * The $raw parameter is only utilized by Mamcache in order to + * allow usage of increment() and decrement(). + * + * @param string $key Cache item name + * @param $value the data to save + * @param null $ttl Time To Live, in seconds (default 60) + * @param bool $raw Whether to store the raw value. + * + * @return mixed + */ + public function save(string $key, $value, int $ttl = 60, bool $raw = false) + { + $key = $this->prefix.$key; + + if ($raw !== true) + { + $value = [$value, time(), $ttl]; + } + + if ($this->memcached instanceof Memcached) + { + return $this->memcached->set($key, $value, $ttl); + } + elseif ($this->memcached instanceof Memcache) + { + return $this->memcached->set($key, $value, 0, $ttl); + } + + return false; + } + + //-------------------------------------------------------------------- + + /** + * Deletes a specific item from the cache store. + * + * @param string $key Cache item name + * + * @return mixed + */ + public function delete(string $key) + { + $key = $this->prefix.$key; + + return $this->memcached->delete($key); + } + + //-------------------------------------------------------------------- + + /** + * Performs atomic incrementation of a raw stored value. + * + * @param string $key Cache ID + * @param int $offset Step/value to increase by + * + * @return mixed + */ + public function increment(string $key, int $offset = 1) + { + $key = $this->prefix.$key; + + return $this->memcached->increment($key, $offset); + } + + //-------------------------------------------------------------------- + + /** + * Performs atomic decrementation of a raw stored value. + * + * @param string $key Cache ID + * @param int $offset Step/value to increase by + * + * @return mixed + */ + public function decrement(string $key, int $offset = 1) + { + $key = $this->prefix.$key; + + return $this->memcached->decrement($key, $offset); + } + + //-------------------------------------------------------------------- + + /** + * Will delete all items in the entire cache. + * + * @return mixed + */ + public function clean() + { + return $this->memcached->flush(); + } + + //-------------------------------------------------------------------- + + /** + * Returns information on the entire cache. + * + * The information returned and the structure of the data + * varies depending on the handler. + * + * @return mixed + */ + public function getCacheInfo() + { + return $this->memcached->getStats(); + } + + //-------------------------------------------------------------------- + + /** + * Returns detailed information about the specific item in the cache. + * + * @param string $key Cache item name. + * + * @return mixed + */ + public function getMetaData(string $key) + { + $key = $this->prefix.$key; + + $stored = $this->memcached->get($key); + + if (count($stored) !== 3) + { + return FALSE; + } + + list($data, $time, $ttl) = $stored; + + return array( + 'expire' => $time + $ttl, + 'mtime' => $time, + 'data' => $data + ); + } + + //-------------------------------------------------------------------- + + /** + * Determines if the driver is supported on this system. + * + * @return boolean + */ + public function isSupported(): bool + { + return (extension_loaded('memcached') OR extension_loaded('memcache')); + } + + //-------------------------------------------------------------------- + + /** + * Class destructor + * + * Closes the connection to Memcache(d) if present. + */ + public function __destruct() + { + if ($this->memcached instanceof Memcache) + { + $this->memcached->close(); + } + elseif ($this->memcached instanceof Memcached) + { + $this->memcached->quit(); + } + } + + //-------------------------------------------------------------------- + +} \ No newline at end of file diff --git a/ci-4.0-dev/system/Cache/Handlers/PredisHandler.php b/ci-4.0-dev/system/Cache/Handlers/PredisHandler.php new file mode 100644 index 000000000..b5bd1db34 --- /dev/null +++ b/ci-4.0-dev/system/Cache/Handlers/PredisHandler.php @@ -0,0 +1,263 @@ + 'tcp', + 'host' => '127.0.0.1', + 'password' => null, + 'port' => 6379, + 'timeout' => 0, + ]; + + /** + * Predis connection + * + * @var Predis + */ + protected $redis; + + //-------------------------------------------------------------------- + + public function __construct($config) + { + $this->prefix = $config->prefix ?: ''; + + if (isset($config->redis)) + { + $this->config = array_merge($this->config, $config->redis); + } + } + + //-------------------------------------------------------------------- + + /** + * Takes care of any handler-specific setup that must be done. + */ + public function initialize() + { + try + { + // Create a new instance of Predis\Client + $this->redis = new \Predis\Client($this->config, ['prefix' => $this->prefix]); + + // Check if the connection is valid by trying to get the time. + $this->redis->time(); + } + catch (Exception $e) + { + // thrown if can't connect to redis server. + throw new CriticalError('Cache: Predis connection refused ('.$e->getMessage().')'); + } + } + + //-------------------------------------------------------------------- + + /** + * Attempts to fetch an item from the cache store. + * + * @param string $key Cache item name + * + * @return mixed + */ + public function get(string $key) + { + $data = array_combine( + ['__ci_type', '__ci_value'], + $this->redis->hmget($key, ['__ci_type', '__ci_value']) + ); + + if (! isset($data['__ci_type'], $data['__ci_value']) OR $data['__ci_value'] === false) + { + return false; + } + + switch ($data['__ci_type']) + { + case 'array': + case 'object': + return unserialize($data['__ci_value']); + case 'boolean': + case 'integer': + case 'double': // Yes, 'double' is returned and NOT 'float' + case 'string': + case 'NULL': + return settype($data['__ci_value'], $data['__ci_type']) + ? $data['__ci_value'] + : false; + case 'resource': + default: + return false; + } + } + + //-------------------------------------------------------------------- + + /** + * Saves an item to the cache store. + * + * The $raw parameter is only utilized by predis in order to + * allow usage of increment() and decrement(). + * + * @param string $key Cache item name + * @param $value the data to save + * @param null $ttl Time To Live, in seconds (default 60) + * @param bool $raw Whether to store the raw value. + * + * @return mixed + */ + public function save(string $key, $value, int $ttl = 60, bool $raw = false) + { + switch ($data_type = gettype($value)) + { + case 'array': + case 'object': + $value = serialize($value); + break; + case 'boolean': + case 'integer': + case 'double': // Yes, 'double' is returned and NOT 'float' + case 'string': + case 'NULL': + break; + case 'resource': + default: + return false; + } + + if (! $this->redis->hmset($key, ['__ci_type' => $data_type, '__ci_value' => $value])) + { + return false; + } + + $this->redis->expireat($key, time()+$ttl); + + return true; + } + + //-------------------------------------------------------------------- + + /** + * Deletes a specific item from the cache store. + * + * @param string $key Cache item name + * + * @return mixed + */ + public function delete(string $key) + { + return ($this->redis->del($key) === 1); + } + + //-------------------------------------------------------------------- + + /** + * Performs atomic incrementation of a raw stored value. + * + * @param string $key Cache ID + * @param int $offset Step/value to increase by + * + * @return mixed + */ + public function increment(string $key, int $offset = 1) + { + return $this->redis->hincrby($key, 'data', $offset); + } + + //-------------------------------------------------------------------- + + /** + * Performs atomic decrementation of a raw stored value. + * + * @param string $key Cache ID + * @param int $offset Step/value to increase by + * + * @return mixed + */ + public function decrement(string $key, int $offset = 1) + { + return $this->redis->hincrby($key, 'data', -$offset); + } + + //-------------------------------------------------------------------- + + /** + * Will delete all items in the entire cache. + * + * @return mixed + */ + public function clean() + { + return $this->redis->flushdb(); + } + + //-------------------------------------------------------------------- + + /** + * Returns information on the entire cache. + * + * The information returned and the structure of the data + * varies depending on the handler. + * + * @return mixed + */ + public function getCacheInfo() + { + return $this->redis->info(); + } + + //-------------------------------------------------------------------- + + /** + * Returns detailed information about the specific item in the cache. + * + * @param string $key Cache item name. + * + * @return mixed + */ + public function getMetaData(string $key) + { + $data = array_combine(['__ci_value'], $this->redis->hmget($key, ['__ci_value'])); + + if (isset($data['__ci_value']) AND $data['__ci_value'] !== false) + { + return array( + 'expire' => time() + $this->redis->ttl($key), + 'data' => $data['__ci_value'] + ); + } + + return FALSE; + } + + //-------------------------------------------------------------------- + + /** + * Determines if the driver is supported on this system. + * + * @return boolean + */ + public function isSupported(): bool + { + return class_exists('\Predis\Client'); + } + + //-------------------------------------------------------------------- + +} \ No newline at end of file diff --git a/ci-4.0-dev/system/Cache/Handlers/RedisHandler.php b/ci-4.0-dev/system/Cache/Handlers/RedisHandler.php new file mode 100644 index 000000000..e10e9d9bc --- /dev/null +++ b/ci-4.0-dev/system/Cache/Handlers/RedisHandler.php @@ -0,0 +1,297 @@ + '127.0.0.1', + 'password' => null, + 'port' => 6379, + 'timeout' => 0, + ]; + + /** + * Redis connection + * + * @var Redis + */ + protected $redis; + + //-------------------------------------------------------------------- + + public function __construct($config) + { + $this->prefix = $config->prefix ?: ''; + + if (isset($config->redis)) + { + $this->config = array_merge($this->config, $config->redis); + } + } + + //-------------------------------------------------------------------- + + /** + * Takes care of any handler-specific setup that must be done. + */ + public function initialize() + { + $config = $this->config; + + $this->redis = new \Redis(); + + try + { + if (! $this->redis->connect($config['host'], ($config['host'][0] === '/' ? 0 + : $config['port']), $config['timeout']) + ) + { +// log_message('error', 'Cache: Redis connection failed. Check your configuration.'); + } + + if (isset($config['password']) && ! $this->redis->auth($config['password'])) + { +// log_message('error', 'Cache: Redis authentication failed.'); + } + } + catch (RedisException $e) + { + throw new CriticalError('Cache: Redis connection refused ('.$e->getMessage().')'); + } + } + + //-------------------------------------------------------------------- + + /** + * Attempts to fetch an item from the cache store. + * + * @param string $key Cache item name + * + * @return mixed + */ + public function get(string $key) + { + $key = $this->prefix.$key; + + $data = $this->redis->hMGet($key, ['__ci_type', '__ci_value']); + + if (! isset($data['__ci_type'], $data['__ci_value']) OR $data['__ci_value'] === false) + { + return false; + } + + switch ($data['__ci_type']) + { + case 'array': + case 'object': + return unserialize($data['__ci_value']); + case 'boolean': + case 'integer': + case 'double': // Yes, 'double' is returned and NOT 'float' + case 'string': + case 'NULL': + return settype($data['__ci_value'], $data['__ci_type']) + ? $data['__ci_value'] + : false; + case 'resource': + default: + return false; + } + } + + //-------------------------------------------------------------------- + + /** + * Saves an item to the cache store. + * + * The $raw parameter is only utilized by Mamcache in order to + * allow usage of increment() and decrement(). + * + * @param string $key Cache item name + * @param $value the data to save + * @param null $ttl Time To Live, in seconds (default 60) + * @param bool $raw Whether to store the raw value. + * + * @return mixed + */ + public function save(string $key, $value, int $ttl = 60, bool $raw = false) + { + $key = $this->prefix.$key; + + switch ($data_type = gettype($value)) + { + case 'array': + case 'object': + $value = serialize($value); + break; + case 'boolean': + case 'integer': + case 'double': // Yes, 'double' is returned and NOT 'float' + case 'string': + case 'NULL': + break; + case 'resource': + default: + return false; + } + + if (! $this->redis->hMSet($key, ['__ci_type' => $data_type, '__ci_value' => $value])) + { + return false; + } + elseif ($ttl) + { + $this->redis->expireAt($key, time()+$ttl); + } + + return true; + } + + //-------------------------------------------------------------------- + + /** + * Deletes a specific item from the cache store. + * + * @param string $key Cache item name + * + * @return mixed + */ + public function delete(string $key) + { + $key = $this->prefix.$key; + + return ($this->redis->delete($key) === 1); + } + + //-------------------------------------------------------------------- + + /** + * Performs atomic incrementation of a raw stored value. + * + * @param string $key Cache ID + * @param int $offset Step/value to increase by + * + * @return mixed + */ + public function increment(string $key, int $offset = 1) + { + $key = $this->prefix.$key; + + return $this->redis->hIncrBy($key, 'data', $offset); + } + + //-------------------------------------------------------------------- + + /** + * Performs atomic decrementation of a raw stored value. + * + * @param string $key Cache ID + * @param int $offset Step/value to increase by + * + * @return mixed + */ + public function decrement(string $key, int $offset = 1) + { + $key = $this->prefix.$key; + + return $this->redis->hIncrBy($key, 'data', -$offset); + } + + //-------------------------------------------------------------------- + + /** + * Will delete all items in the entire cache. + * + * @return mixed + */ + public function clean() + { + return $this->redis->flushDB(); + } + + //-------------------------------------------------------------------- + + /** + * Returns information on the entire cache. + * + * The information returned and the structure of the data + * varies depending on the handler. + * + * @return mixed + */ + public function getCacheInfo() + { + return $this->redis->info(); + } + + //-------------------------------------------------------------------- + + /** + * Returns detailed information about the specific item in the cache. + * + * @param string $key Cache item name. + * + * @return mixed + */ + public function getMetaData(string $key) + { + $key = $this->prefix.$key; + + $value = $this->get($key); + + if ($value !== FALSE) + { + return array( + 'expire' => time() + $this->redis->ttl($key), + 'data' => $value + ); + } + + return FALSE; + } + + //-------------------------------------------------------------------- + + /** + * Determines if the driver is supported on this system. + * + * @return boolean + */ + public function isSupported(): bool + { + return extension_loaded('redis'); + } + + //-------------------------------------------------------------------- + + /** + * Class destructor + * + * Closes the connection to Memcache(d) if present. + */ + public function __destruct() + { + if ($this->redis) + { + $this->redis->close(); + } + } + + //-------------------------------------------------------------------- + +} \ No newline at end of file diff --git a/ci-4.0-dev/system/Cache/Handlers/WincacheHandler.php b/ci-4.0-dev/system/Cache/Handlers/WincacheHandler.php new file mode 100644 index 000000000..adac0d91a --- /dev/null +++ b/ci-4.0-dev/system/Cache/Handlers/WincacheHandler.php @@ -0,0 +1,200 @@ +prefix = $config->prefix ?: ''; + } + + //-------------------------------------------------------------------- + + /** + * Takes care of any handler-specific setup that must be done. + */ + public function initialize() + { + // Nothing to see here... + } + + //-------------------------------------------------------------------- + + /** + * Attempts to fetch an item from the cache store. + * + * @param string $key Cache item name + * + * @return mixed + */ + public function get(string $key) + { + $key = $this->prefix.$key; + + $success = false; + $data = wincache_ucache_get($key, $success); + + // Success returned by reference from wincache_ucache_get() + return ($success) ? $data : false; + } + + //-------------------------------------------------------------------- + + /** + * Saves an item to the cache store. + * + * The $raw parameter is only utilized by Mamcache in order to + * allow usage of increment() and decrement(). + * + * @param string $key Cache item name + * @param $value the data to save + * @param null $ttl Time To Live, in seconds (default 60) + * @param bool $raw Whether to store the raw value. + * + * @return mixed + */ + public function save(string $key, $value, int $ttl = 60, bool $raw = false) + { + $key = $this->prefix.$key; + + return wincache_ucache_set($key, $value, $ttl); + } + + //-------------------------------------------------------------------- + + /** + * Deletes a specific item from the cache store. + * + * @param string $key Cache item name + * + * @return mixed + */ + public function delete(string $key) + { + $key = $this->prefix.$key; + + return wincache_ucache_delete($key); + } + + //-------------------------------------------------------------------- + + /** + * Performs atomic incrementation of a raw stored value. + * + * @param string $key Cache ID + * @param int $offset Step/value to increase by + * + * @return mixed + */ + public function increment(string $key, int $offset = 1) + { + $key = $this->prefix.$key; + + $success = false; + $value = wincache_ucache_inc($key, $offset, $success); + + return ($success === true) ? $value : false; + } + + //-------------------------------------------------------------------- + + /** + * Performs atomic decrementation of a raw stored value. + * + * @param string $key Cache ID + * @param int $offset Step/value to increase by + * + * @return mixed + */ + public function decrement(string $key, int $offset = 1) + { + $key = $this->prefix.$key; + + $success = false; + $value = wincache_ucache_dec($key, $offset, $success); + + return ($success === true) ? $value : false; + } + + //-------------------------------------------------------------------- + + /** + * Will delete all items in the entire cache. + * + * @return mixed + */ + public function clean() + { + return wincache_ucache_clear(); + } + + //-------------------------------------------------------------------- + + /** + * Returns information on the entire cache. + * + * The information returned and the structure of the data + * varies depending on the handler. + * + * @return mixed + */ + public function getCacheInfo() + { + return wincache_ucache_info(true); + } + + //-------------------------------------------------------------------- + + /** + * Returns detailed information about the specific item in the cache. + * + * @param string $key Cache item name. + * + * @return mixed + */ + public function getMetaData(string $key) + { + $key = $this->prefix.$key; + + if ($stored = wincache_ucache_info(false, $key)) + { + $age = $stored['ucache_entries'][1]['age_seconds']; + $ttl = $stored['ucache_entries'][1]['ttl_seconds']; + $hitcount = $stored['ucache_entries'][1]['hitcount']; + + return [ + 'expire' => $ttl-$age, + 'hitcount' => $hitcount, + 'age' => $age, + 'ttl' => $ttl, + ]; + } + + return false; + } + + //-------------------------------------------------------------------- + + /** + * Determines if the driver is supported on this system. + * + * @return boolean + */ + public function isSupported(): bool + { + return (extension_loaded('wincache') && ini_get('wincache.ucenabled')); + } + + //-------------------------------------------------------------------- + +} \ No newline at end of file diff --git a/ci-4.0-dev/system/CodeIgniter.php b/ci-4.0-dev/system/CodeIgniter.php new file mode 100644 index 000000000..b1999158a --- /dev/null +++ b/ci-4.0-dev/system/CodeIgniter.php @@ -0,0 +1,761 @@ +startMemory = $startMemory; + $this->startTime = $startTime; + $this->config = $config; + } + + //-------------------------------------------------------------------- + + /** + * The class entry point. This is where the magic happens and all + * of the framework pieces are pulled together and shown how to + * make beautiful music together. Or something like that. :) + * + * @param RouteCollectionInterface $routes + */ + public function run(RouteCollectionInterface $routes = null) + { + $this->startBenchmark(); + + //-------------------------------------------------------------------- + // Is there a "pre-system" hook? + //-------------------------------------------------------------------- + Hooks::trigger('pre_system'); + + $this->getRequestObject(); + $this->getResponseObject(); + + // Check for a cached page. Execution will stop + // if the page has been cached. + $cacheConfig = new Cache(); + $this->displayCache($cacheConfig); + + $this->forceSecureAccess(); + + try + { + $this->tryToRouteIt($routes); + + //-------------------------------------------------------------------- + // Run "before" filters + //-------------------------------------------------------------------- + $filters = Services::filters(); + $uri = $this->request instanceof CLIRequest + ? $this->request->getPath() + : $this->request->uri->getPath(); + + $filters->run($uri, 'before'); + + $returned = $this->startController(); + + // Closure controller has run in startController(). + if ( ! is_callable($this->controller)) + { + $controller = $this->createController(); + + //-------------------------------------------------------------------- + // Is there a "post_controller_constructor" hook? + //-------------------------------------------------------------------- + Hooks::trigger('post_controller_constructor'); + + $returned = $this->runController($controller); + } + + // If $returned is a string, then the controller output something, + // probably a view, instead of echoing it directly. Send it along + // so it can be used with the output. + $this->gatherOutput($cacheConfig, $returned); + + //-------------------------------------------------------------------- + // Run "after" filters + //-------------------------------------------------------------------- + $response = $filters->run($uri, 'after'); + + if ($response instanceof Response) + { + $this->response = $response; + } + + // Save our current URI as the previous URI in the session + // for safer, more accurate use with `previous_url()` helper function. + $this->storePreviousURL($this->request->uri ?? $uri); + + unset($uri); + + $this->sendResponse(); + + //-------------------------------------------------------------------- + // Is there a post-system hook? + //-------------------------------------------------------------------- + Hooks::trigger('post_system'); + } + catch (Router\RedirectException $e) + { + $logger = Services::logger(); + $logger->info('REDIRECTED ROUTE at '.$e->getMessage()); + + // If the route is a 'redirect' route, it throws + // the exception with the $to as the message + $this->response->redirect($e->getMessage(), 'auto', $e->getCode()); + $this->callExit(EXIT_SUCCESS); + } + // Catch Response::redirect() + catch (HTTP\RedirectException $e) + { + $this->callExit(EXIT_SUCCESS); + } + catch (PageNotFoundException $e) + { + $this->display404errors($e); + } + } + + //-------------------------------------------------------------------- + + /** + * Determines if a response has been cached for the given URI. + * + * @param \Config\Cache $config + * + * @return bool + */ + public function displayCache($config) + { + if ($cachedResponse = cache()->get($this->generateCacheName($config))) + { + $cachedResponse = unserialize($cachedResponse); + if (!is_array($cachedResponse) || !isset($cachedResponse['output']) || !isset($cachedResponse['headers'])) + { + throw new \Exception("Error unserializing page cache"); + } + + $headers = $cachedResponse['headers']; + $output = $cachedResponse['output']; + + // Clear all default headers + foreach($this->response->getHeaders() as $key => $val) { + $this->response->removeHeader($key); + } + + // Set cached headers + foreach($headers as $name => $value) { + $this->response->setHeader($name, $value); + } + + $output = $this->displayPerformanceMetrics($output); + $this->response->setBody($output)->send(); + $this->callExit(EXIT_SUCCESS); + }; + } + + + //-------------------------------------------------------------------- + + /** + * Tells the app that the final output should be cached. + * + * @param int $time + * + * @return $this + */ + public static function cache(int $time) + { + self::$cacheTTL = (int)$time; + } + + //-------------------------------------------------------------------- + + /** + * Caches the full response from the current request. Used for + * full-page caching for very high performance. + * + * @param \Config\Cache $config + */ + public function cachePage(Cache $config) + { + $headers = []; + foreach($this->response->getHeaders() as $header) { + $headers[$header->getName()] = $header->getValueLine(); + } + + return cache()->save( + $this->generateCacheName($config), + serialize(['headers' => $headers, 'output' => $this->output]), + self::$cacheTTL + ); + + } + + //-------------------------------------------------------------------- + + public function getPerfomanceStats() + { + return [ + 'startTime' => $this->startTime, + 'totalTime' => $this->totalTime, + 'startMemory' => $this->startMemory + ]; + } + + //-------------------------------------------------------------------- + + /** + * Generates the cache name to use for our full-page caching. + * + * @param \CodeIgniter\HTTP\URI $URI + * + * @return string + */ + protected function generateCacheName($config): string + { + if (is_cli()) + { + return md5($this->request->getPath()); + } + + $uri = $this->request->uri; + + if ($config->cacheQueryString) + { + $name = URI::createURIString( + $uri->getScheme(), + $uri->getAuthority(), + $uri->getPath(), + $uri->getQuery() + ); + } + else + { + $name = URI::createURIString( + $uri->getScheme(), + $uri->getAuthority(), + $uri->getPath() + ); + } + + return md5($name); + } + + //-------------------------------------------------------------------- + + /** + * Replaces the memory_usage and elapsed_time tags. + * + * @param string $output + * + * @return string + */ + public function displayPerformanceMetrics(string $output): string + { + $this->totalTime = $this->benchmark->getElapsedTime('total_execution'); + + $output = str_replace('{elapsed_time}', $this->totalTime, $output); + + return $output; + } + + //-------------------------------------------------------------------- + + /** + * Start the Benchmark + * + * The timer is used to display total script execution both in the + * debug toolbar, and potentially on the displayed page. + */ + protected function startBenchmark() + { + $this->startTime = microtime(true); + + $this->benchmark = Services::timer(); + $this->benchmark->start('total_execution', $this->startTime); + $this->benchmark->start('bootstrap'); + } + + //-------------------------------------------------------------------- + + /** + * Get our Request object, (either IncomingRequest or CLIRequest) + * and set the server protocol based on the information provided + * by the server. + */ + protected function getRequestObject() + { + if (is_cli()) + { + $this->request = Services::clirequest($this->config); + } + else + { + $this->request = Services::request($this->config); + $this->request->setProtocolVersion($_SERVER['SERVER_PROTOCOL']); + } + } + + //-------------------------------------------------------------------- + + /** + * Get our Response object, and set some default values, including + * the HTTP protocol version and a default successful response. + */ + protected function getResponseObject() + { + $this->response = Services::response($this->config); + + if ( ! is_cli()) + { + $this->response->setProtocolVersion($this->request->getProtocolVersion()); + } + + // Assume success until proven otherwise. + $this->response->setStatusCode(200); + } + + //-------------------------------------------------------------------- + + /** + * Force Secure Site Access? If the config value 'forceGlobalSecureRequests' + * is true, will enforce that all requests to this site are made through + * HTTPS. Will redirect the user to the current page with HTTPS, as well + * as set the HTTP Strict Transport Security header for those browsers + * that support it. + * + * @param int $duration How long the Strict Transport Security + * should be enforced for this URL. + */ + protected function forceSecureAccess($duration = 31536000) + { + if ($this->config->forceGlobalSecureRequests !== true) + { + return; + } + + force_https($duration, $this->request, $this->response); + } + + //-------------------------------------------------------------------- + + /** + * Try to Route It - As it sounds like, works with the router to + * match a route against the current URI. If the route is a + * "redirect route", will also handle the redirect. + * + * @param RouteCollectionInterface $routes An collection interface to use in place + * of the config file. + */ + protected function tryToRouteIt(RouteCollectionInterface $routes = null) + { + if (empty($routes) || ! $routes instanceof RouteCollectionInterface) + { + require APPPATH.'Config/Routes.php'; + } + + // $routes is defined in Config/Routes.php + $this->router = Services::router($routes); + + $path = is_cli() ? $this->request->getPath() : $this->request->uri->getPath(); + + $this->benchmark->stop('bootstrap'); + $this->benchmark->start('routing'); + + ob_start(); + + $this->controller = $this->router->handle($path); + $this->method = $this->router->methodName(); + + // If a {locale} segment was matched in the final route, + // then we need to set the correct locale on our Request. + if ($this->router->hasLocale()) + { + $this->request->setLocale($this->router->getLocale()); + } + + $this->benchmark->stop('routing'); + } + + //-------------------------------------------------------------------- + + /** + * Now that everything has been setup, this method attempts to run the + * controller method and make the script go. If it's not able to, will + * show the appropriate Page Not Found error. + */ + protected function startController() + { + $this->benchmark->start('controller'); + $this->benchmark->start('controller_constructor'); + + // Is it routed to a Closure? + if (is_object($this->controller) && (get_class($this->controller) == 'Closure')) + { + $controller = $this->controller; + return $controller(...$this->router->params()); + } + else + { + // No controller specified - we don't know what to do now. + if (empty($this->controller)) + { + throw new PageNotFoundException('Controller is empty.'); + } + else + { + // Try to autoload the class + if ( ! class_exists($this->controller, true) || $this->method[0] === '_') + { + throw new PageNotFoundException('Controller or its method is not found.'); + } + else if ( ! method_exists($this->controller, '_remap') && + ! is_callable([$this->controller, $this->method], false) + ) + { + throw new PageNotFoundException('Controller method is not found.'); + } + } + } + } + + //-------------------------------------------------------------------- + + /** + * Instantiates the controller class. + * + * @return mixed + */ + protected function createController() + { + $class = new $this->controller($this->request, $this->response); + + $this->benchmark->stop('controller_constructor'); + + return $class; + } + + //-------------------------------------------------------------------- + + /** + * Runs the controller, allowing for _remap methods to function. + * + * @param mixed $class + * + * @return mixed + */ + protected function runController($class) + { + if (method_exists($class, '_remap')) + { + $output = $class->_remap($this->method, ...$this->router->params()); + } + else + { + $output = $class->{$this->method}(...$this->router->params()); + } + + $this->benchmark->stop('controller'); + + return $output; + } + + //-------------------------------------------------------------------- + + /** + * Displays a 404 Page Not Found error. If set, will try to + * call the 404Override controller/method that was set in routing config. + * + * @param PageNotFoundException $e + */ + protected function display404errors(PageNotFoundException $e) + { + // Is there a 404 Override available? + if ($override = $this->router->get404Override()) + { + if ($override instanceof \Closure) + { + echo $override(); + } + else if (is_array($override)) + { + $this->benchmark->start('controller'); + $this->benchmark->start('controller_constructor'); + + $this->controller = $override[0]; + $this->method = $override[1]; + + unset($override); + + $controller = $this->createController(); + $this->runController($controller); + } + + $this->gatherOutput(); + $this->sendResponse(); + + return; + } + + // Display 404 Errors + $this->response->setStatusCode(404); + + if (ENVIRONMENT !== 'testing') { + if (ob_get_level() > 0) { + ob_end_flush(); + } + } + else + { + // When testing, one is for phpunit, another is for test case. + if (ob_get_level() > 2) { + ob_end_flush(); + } + } + + ob_start(); + + // These might show as unused here - but don't delete! + // They are used within the view files. + $heading = 'Page Not Found'; + $message = $e->getMessage(); + + // Show the 404 error page + if (is_cli()) + { + require APPPATH.'Views/errors/cli/error_404.php'; + } + else + { + require APPPATH.'Views/errors/html/error_404.php'; + } + + $buffer = ob_get_contents(); + ob_end_clean(); + + echo $buffer; + $this->callExit(EXIT_UNKNOWN_FILE); // Unknown file + } + + //-------------------------------------------------------------------- + + /** + * Gathers the script output from the buffer, replaces some execution + * time tag in the output and displays the debug toolbar, if required. + */ + protected function gatherOutput($cacheConfig = null, $returned = null) + { + $this->output = ob_get_contents(); + ob_end_clean(); + + if (is_string($returned)) + { + $this->output .= $returned; + } + + // Cache it without the performance metrics replaced + // so that we can have live speed updates along the way. + if (self::$cacheTTL > 0) + { + $this->cachePage($cacheConfig); + } + + $this->output = $this->displayPerformanceMetrics($this->output); + + $this->response->setBody($this->output); + } + + //-------------------------------------------------------------------- + + /** + * If we have a session object to use, store the current URI + * as the previous URI. This is called just prior to sending the + * response to the client, and will make it available next request. + * + * This helps provider safer, more reliable previous_url() detection. + * + * @param \CodeIgniter\HTTP\URI $uri + */ + public function storePreviousURL($uri) + { + // This is mainly needed during testing... + if (is_string($uri)) + { + $uri = new URI($uri); + } + + if (isset($_SESSION)) + { + $_SESSION['_ci_previous_url'] = (string)$uri; + } + } + + //-------------------------------------------------------------------- + + /** + * Sends the output of this request back to the client. + * This is what they've been waiting for! + */ + protected function sendResponse() + { + $this->response->send(); + } + + //-------------------------------------------------------------------- + + /** + * Exits the application, setting the exit code for CLI-based applications + * that might be watching. + * + * Made into a separate method so that it can be mocked during testing + * without actually stopping script execution. + * + * @param $code + */ + protected function callExit($code) + { + exit($code); + } + + //-------------------------------------------------------------------- +} diff --git a/ci-4.0-dev/system/Commands/MigrationsCommand.php b/ci-4.0-dev/system/Commands/MigrationsCommand.php new file mode 100644 index 000000000..a323273a9 --- /dev/null +++ b/ci-4.0-dev/system/Commands/MigrationsCommand.php @@ -0,0 +1,330 @@ +runner = Services::migrations(); + } + + //-------------------------------------------------------------------- + + /** + * Provides a list of available commands. + */ + public function index() + { + CLI::write('Migration Commands', 'white'); + CLI::write(CLI::color('latest', 'yellow'). lang('Migrations.migHelpLatest')); + CLI::write(CLI::color('current', 'yellow'). lang('Migrations.migHelpCurrent')); + CLI::write(CLI::color('version [v]', 'yellow'). lang('Migrations.migHelpVersion')); + CLI::write(CLI::color('rollback', 'yellow'). lang('Migrations.migHelpRollback')); + CLI::write(CLI::color('refresh', 'yellow'). lang('Migrations.migHelpRefresh')); + CLI::write(CLI::color('seed [name]', 'yellow'). lang('Migrations.migHelpSeed')); + CLI::write(CLI::color('create [name]', 'yellow'). lang('Migrations.migCreate')); + } + + //-------------------------------------------------------------------- + + + /** + * Ensures that all migrations have been run. + */ + public function latest() + { + CLI::write(lang('Migrations.migToLatest'), 'yellow'); + + try { + $this->runner->latest(); + } + catch (\Exception $e) + { + $this->showError($e); + } + + CLI::write('Done'); + } + + //-------------------------------------------------------------------- + + /** + * Migrates the database up or down to get to the specified version. + * + * @param int $version + */ + public function version(int $version = null) + { + if (is_null($version)) + { + $version = CLI::prompt(lang('Migrations.version')); + } + + if (is_null($version)) + { + CLI::error(lang('Migrations.invalidVersion')); + exit(); + } + + CLI::write(sprintf(lang('Migrations.migToVersionPH'), $version), 'yellow'); + + try { + $this->runner->version($version); + } + catch (\Exception $e) + { + $this->showError($e); + } + + CLI::write('Done'); + } + + //-------------------------------------------------------------------- + + /** + * Migrates us up or down to the version specified as $currentVersion + * in the migrations config file. + */ + public function current() + { + CLI::write(lang('Migrations.migToVersion'), 'yellow'); + + try { + $this->runner->current(); + } + catch (\Exception $e) + { + $this->showError($e); + } + + CLI::write('Done'); + } + + //-------------------------------------------------------------------- + + /** + * Runs all of the migrations in reverse order, until they have + * all been un-applied. + */ + public function rollback() + { + CLI::write(lang('Migrations.migRollingBack'), 'yellow'); + + try { + $this->runner->version(0); + } + catch (\Exception $e) + { + $this->showError($e); + } + + CLI::write('Done'); + } + + //-------------------------------------------------------------------- + + /** + * Does a rollback followed by a latest to refresh the current state + * of the database. + */ + public function refresh() + { + $this->rollback(); + $this->latest(); + } + + //-------------------------------------------------------------------- + + /** + * Displays a list of all migrations and whether they've been run or not. + */ + public function status() + { + $migrations = $this->runner->findMigrations(); + $history = $this->runner->getHistory(); + + if (empty($migrations)) + { + return CLI::error(lang('Migrations.migNoneFound')); + } + + $max = 0; + + foreach ($migrations as $version => $file) + { + $file = substr($file, strpos($file, $version.'_')); + $migrations[$version] = $file; + + $max = max($max, strlen($file)); + } + + CLI::write(str_pad(lang('Migrations.filename'), $max+4).lang('Migrations.migOn'), 'yellow'); + + foreach ($migrations as $version => $file) + { + $date = ''; + foreach ($history as $row) + { + if ($row['version'] != $version) continue; + + $date = $row['time']; + } + + CLI::write(str_pad($file, $max+4). ($date ? $date : '---')); + } + } + + //-------------------------------------------------------------------- + + /** + * Runs the specified Seeder file to populate the database + * with some data. + * + * @param string $seedName + */ + public function seed(string $seedName = null) + { + $seeder = new Seeder(new \Config\Database()); + + if (empty($seedName)) + { + $seedName = CLI::prompt(lang('Migrations.migSeeder'), 'DatabaseSeeder'); + } + + if (empty($seedName)) + { + CLI::error(lang('Migrations.migMissingSeeder')); + return; + } + + try + { + $seeder->call($seedName); + } + catch (\Exception $e) + { + $this->showError($e); + } + } + + //-------------------------------------------------------------------- + + public function create(string $name = null) + { + if (empty($name)) + { + $name = CLI::prompt(lang('Migrations.migNameMigration')); + } + + if (empty($name)) + { + CLI::error(lang('Migrations.migBadCreateName')); + return; + } + + $path = APPPATH.'Database/Migrations/'.date('YmdHis_').$name.'.php'; + + $template =<<getMessage()); + CLI::write($e->getFile().' - '.$e->getLine(), 'white'); + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Common.php b/ci-4.0-dev/system/Common.php new file mode 100644 index 000000000..84b210f60 --- /dev/null +++ b/ci-4.0-dev/system/Common.php @@ -0,0 +1,761 @@ +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 = false; + 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); + } +} + +//-------------------------------------------------------------------- + +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; + } +} + +//-------------------------------------------------------------------- + +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(); + } +} + +//-------------------------------------------------------------------- + +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); + } +} + +//-------------------------------------------------------------------- + +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); + } +} + +//-------------------------------------------------------------------- + +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); + } +} + +//-------------------------------------------------------------------- + +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); + } +} + +//-------------------------------------------------------------------- + + + +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); + } +} + +//-------------------------------------------------------------------- + +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')); + } +} + +//-------------------------------------------------------------------- + +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); + } +} + +//-------------------------------------------------------------------- + +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; + } +} + +//-------------------------------------------------------------------- + +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); + + if (! is_array($filenames)) + { + $filenames = [$filenames]; + } + + foreach ($filenames as $filename) + { + if (strpos($filename, '_helper') === false) + { + $filename .= '_helper'; + } + + $path = $loader->locateFile($filename, 'Helpers'); + + if (! empty($path)) + { + 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; + } +} + +//-------------------------------------------------------------------- + +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; + } +} + +//-------------------------------------------------------------------- + +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(); + } +} + +//-------------------------------------------------------------------- + +if (! function_exists('csrf_field')) +{ + /** + * Generates a hidden input field for use within manually generated forms. + * + * @return string + */ + function csrf_field() + { + return ''; + } +} + +//-------------------------------------------------------------------- + +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(); + } +} + +//-------------------------------------------------------------------- + +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); + } +} + +//-------------------------------------------------------------------- + +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) + ? $key.'='.esc($val, 'js').',' + : ' '.$key.'="'.esc($val, 'attr').'"'; + } + + return rtrim($atts, ','); + } +} + +//-------------------------------------------------------------------- + +if ( ! function_exists('is_really_writable')) +{ + /** + * Tests for file writability + * + * is_writable() returns TRUE on Windows servers when you really can't write to + * 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 + */ + function is_really_writable($file) + { + // If we're on a Unix server with safe_mode off we call is_writable + if (DIRECTORY_SEPARATOR === '/' || ! ini_get('safe_mode')) + { + return is_writable($file); + } + + /* For Windows servers and safe_mode "on" installations we'll actually + * write a file then read it. Bah... + */ + if (is_dir($file)) + { + $file = rtrim($file, '/').'/'.md5(mt_rand()); + if (($fp = @fopen($file, 'ab')) === FALSE) + { + return FALSE; + } + + fclose($fp); + @chmod($file, 0777); + @unlink($file); + return TRUE; + } + elseif ( ! is_file($file) OR ($fp = @fopen($file, 'ab')) === FALSE) + { + return FALSE; + } + + fclose($fp); + return TRUE; + } +} + +//-------------------------------------------------------------------- + +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/ci-4.0-dev/system/ComposerScripts.php b/ci-4.0-dev/system/ComposerScripts.php new file mode 100644 index 000000000..6d8a6bf38 --- /dev/null +++ b/ci-4.0-dev/system/ComposerScripts.php @@ -0,0 +1,133 @@ + $base.'/Exception/ExceptionInterface.php', + self::getClassFilePath('\\Zend\\Escaper\\Exception\\InvalidArgumentException') => $base.'/Exception/InvalidArgumentException.php', + self::getClassFilePath('\\Zend\\Escaper\\Exception\\RuntimeException') => $base.'/Exception/RuntimeException.php', + self::getClassFilePath('\\Zend\\Escaper\\Escaper') => $base.'/Escaper.php' + ]; + + foreach ($files as $source => $dest) + { + if (! self::moveFile($source, $dest)) + { + die('Error moving: '. $source); + } + } + } + } + + //-------------------------------------------------------------------- + + /** + * Move a file. + * + * @param string $source + * @param string $destination + * @return boolean + */ + protected static function moveFile(string $source, string $destination) + { + $source = realpath($source); + + if (empty($source)) + { + die('Cannot move file. Source path invalid.'); + } + + if (! is_file($source)) + { + return false; + } + + return rename($source, $destination); + } + + //-------------------------------------------------------------------- + + /** + * Determine file path of a class. + * + * @param string $class + * @return type + */ + protected static function getClassFilePath( string $class ) + { + $reflector = new \ReflectionClass($class); + return $reflector->getFileName(); + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Config/AutoloadConfig.php b/ci-4.0-dev/system/Config/AutoloadConfig.php new file mode 100644 index 000000000..1bc10a36d --- /dev/null +++ b/ci-4.0-dev/system/Config/AutoloadConfig.php @@ -0,0 +1,189 @@ + SYSPATH + * `]; + */ + $this->psr4 = [ + 'CodeIgniter' => realpath(BASEPATH) + ]; + + if (ENVIRONMENT == 'testing') + { + $this->psr4['Tests\Support'] = BASEPATH.'../tests/_support/'; + } + + /** + * ------------------------------------------------------------------- + * Class Map + * ------------------------------------------------------------------- + * The class map provides a map of class names and their exact + * location on the drive. Classes loaded in this manner will have + * slightly faster performance because they will not have to be + * searched for within one or more directories as they would if they + * were being autoloaded through a namespace. + * + * Prototype: + * + * $Config['classmap'] = [ + * 'MyClass' => '/path/to/class/file.php' + * ]; + */ + $this->classmap = [ + 'CodeIgniter\CodeIgniter' => BASEPATH.'CodeIgniter.php', + 'CodeIgniter\CLI\CLI' => BASEPATH.'CLI/CLI.php', + 'CodeIgniter\Loader' => BASEPATH.'Loader.php', + 'CodeIgniter\Cache\CacheFactory' => BASEPATH.'Cache/CacheFactory.php', + 'CodeIgniter\Cache\CacheInterface' => BASEPATH.'Cache/CacheInterface.php', + 'CodeIgniter\Cache\Handlers\DummyHandler' => BASEPATH.'Cache/Handlers/DummyHandler.php', + 'CodeIgniter\Cache\Handlers\FileHandler' => BASEPATH.'Cache/Handlers/FileHandler.php', + 'CodeIgniter\Cache\Handlers\MemcachedHandler' => BASEPATH.'Cache/Handlers/MemcachedHandler.php', + 'CodeIgniter\Cache\Handlers\PredisHandler' => BASEPATH.'Cache/Handlers/PredisHandler.php', + 'CodeIgniter\Cache\Handlers\RedisHandler' => BASEPATH.'Cache/Handlers/RedisHandler.php', + 'CodeIgniter\Cache\Handlers\WincacheHandler' => BASEPATH.'Cache/Handlers/WincacheHandler.php', + 'CodeIgniter\Controller' => BASEPATH.'Controller.php', + 'CodeIgniter\Config\AutoloadConfig' => BASEPATH.'Config/Autoload.php', + 'CodeIgniter\Config\BaseConfig' => BASEPATH.'Config/BaseConfig.php', + 'CodeIgniter\Config\Database' => BASEPATH.'Config/Database.php', + 'CodeIgniter\Config\Database\Connection' => BASEPATH.'Config/Database/Connection.php', + 'CodeIgniter\Config\Database\Connection\MySQLi' => BASEPATH.'Config/Database/Connection/MySQLi.php', + 'CodeIgniter\Config\DotEnv' => BASEPATH.'Config/DotEnv.php', + 'CodeIgniter\Database\BaseBuilder' => BASEPATH.'Database/BaseBuilder.php', + 'CodeIgniter\Database\BaseConnection' => BASEPATH.'Database/BaseConnection.php', + 'CodeIgniter\Database\BaseResult' => BASEPATH.'Database/BaseResult.php', + 'CodeIgniter\Database\Config' => BASEPATH.'Database/Config.php', + 'CodeIgniter\Database\ConnectionInterface' => BASEPATH.'Database/ConnectionInterface.php', + 'CodeIgniter\Database\Database' => BASEPATH.'Database/Database.php', + 'CodeIgniter\Database\Query' => BASEPATH.'Database/Query.php', + 'CodeIgniter\Database\QueryInterface' => BASEPATH.'Database/QueryInterface.php', + 'CodeIgniter\Database\ResultInterface' => BASEPATH.'Database/ResultInterface.php', + 'CodeIgniter\Database\Migration' => BASEPATH.'Database/Migration.php', + 'CodeIgniter\Database\MigrationRunner' => BASEPATH.'Database/MigrationRunner.php', + 'CodeIgniter\Debug\Exceptions' => BASEPATH.'Debug/Exceptions.php', + 'CodeIgniter\Debug\Timer' => BASEPATH.'Debug/Timer.php', + 'CodeIgniter\Debug\Iterator' => BASEPATH.'Debug/Iterator.php', + 'CodeIgniter\Hooks\Hooks' => BASEPATH.'Hooks/Hooks.php', + 'CodeIgniter\HTTP\CLIRequest' => BASEPATH.'HTTP/CLIRequest.php', + 'CodeIgniter\HTTP\ContentSecurityPolicy' => BASEPATH.'HTTP/ContentSecurityPolicy.php', + 'CodeIgniter\HTTP\CURLRequest' => BASEPATH.'HTTP/CURLRequest.php', + 'CodeIgniter\HTTP\IncomingRequest' => BASEPATH.'HTTP/IncomingRequest.php', + 'CodeIgniter\HTTP\Message' => BASEPATH.'HTTP/Message.php', + 'CodeIgniter\HTTP\Negotiate' => BASEPATH.'HTTP/Negotiate.php', + 'CodeIgniter\HTTP\Request' => BASEPATH.'HTTP/Request.php', + 'CodeIgniter\HTTP\RequestInterface' => BASEPATH.'HTTP/RequestInterface.php', + 'CodeIgniter\HTTP\Response' => BASEPATH.'HTTP/Response.php', + 'CodeIgniter\HTTP\ResponseInterface' => BASEPATH.'HTTP/ResponseInterface.php', + 'CodeIgniter\HTTP\URI' => BASEPATH.'HTTP/URI.php', + 'CodeIgniter\Log\Logger' => BASEPATH.'Log/Logger.php', + 'Psr\Log\LoggerAwareInterface' => BASEPATH.'ThirdParty/PSR/Log/LoggerAwareInterface.php', + 'Psr\Log\LoggerAwareTrait' => BASEPATH.'ThirdParty/PSR/Log/LoggerAwareTrait.php', + 'Psr\Log\LoggerInterface' => BASEPATH.'ThirdParty/PSR/Log/LoggerInterface.php', + 'Psr\Log\LogLevel' => BASEPATH.'ThirdParty/PSR/Log/LogLevel.php', + 'CodeIgniter\Log\Handlers\BaseHandler' => BASEPATH.'Log/Handlers/BaseHandler.php', + 'CodeIgniter\Log\Handlers\ChromeLoggerHandler' => BASEPATH.'Log/Handlers/ChromeLoggerHandler.php', + 'CodeIgniter\Log\Handlers\FileHandler' => BASEPATH.'Log/Handlers/FileHandler.php', + 'CodeIgniter\Log\Handlers\HandlerInterface' => BASEPATH.'Log/Handlers/HandlerInterface.php', + 'CodeIgniter\Router\RouteCollection' => BASEPATH.'Router/RouteCollection.php', + 'CodeIgniter\Router\RouteCollectionInterface' => BASEPATH.'Router/RouteCollectionInterface.php', + 'CodeIgniter\Router\Router' => BASEPATH.'Router/Router.php', + 'CodeIgniter\Router\RouterInterface' => BASEPATH.'Router/RouterInterface.php', + 'CodeIgniter\Security\Security' => BASEPATH.'Security/Security.php', + 'CodeIgniter\Session\Session' => BASEPATH.'Session/Session.php', + 'CodeIgniter\Session\SessionInterface' => BASEPATH.'Session/SessionInterface.php', + 'CodeIgniter\Session\Handlers\BaseHandler' => BASEPATH.'Session/Handlers/BaseHandler.php', + 'CodeIgniter\Session\Handlers\FileHandler' => BASEPATH.'Session/Handlers/FileHandler.php', + 'CodeIgniter\Session\Handlers\MemcachedHandler' => BASEPATH.'Session/Handlers/MemcachedHandler.php', + 'CodeIgniter\Session\Handlers\RedisHandler' => BASEPATH.'Session/Handlers/RedisHandler.php', + 'CodeIgniter\View\RendererInterface' => BASEPATH.'View/RendererInterface.php', + 'CodeIgniter\View\View' => BASEPATH.'View/View.php', + 'CodeIgniter\View\Parser' => BASEPATH.'View/Parser.php', + 'CodeIgniter\View\Cell' => BASEPATH.'View/Cell.php', + 'Zend\Escaper\Escaper' => BASEPATH.'ThirdParty/ZendEscaper/Escaper.php', + 'CodeIgniter\Log\TestLogger' => BASEPATH.'../tests/_support/Log/TestLogger.php', + 'CIDatabaseTestCase' => BASEPATH.'../tests/_support/CIDatabaseTestCase.php' + ]; + } + + //-------------------------------------------------------------------- +} diff --git a/ci-4.0-dev/system/Config/BaseConfig.php b/ci-4.0-dev/system/Config/BaseConfig.php new file mode 100644 index 000000000..e3dbad46b --- /dev/null +++ b/ci-4.0-dev/system/Config/BaseConfig.php @@ -0,0 +1,121 @@ +$property)) + { + foreach ($this->$property as $key => $val) + { + if ($value = $this->getEnvValue("{$property}.{$key}", $prefix, $shortPrefix)) + { + if (is_null($value)) continue; + + if ($value === 'false') $value = false; + elseif ($value === 'true') $value = true; + + $this->$property[$key] = $value; + } + } + } + else + { + if (($value = $this->getEnvValue($property, $prefix, $shortPrefix)) !== false ) + { + if (is_null($value)) continue; + + if ($value === 'false') $value = false; + elseif ($value === 'true') $value = true; + + $this->$property = $value; + } + } + } + } + + //-------------------------------------------------------------------- + + /** + * Retrieve an environment-specific configuration setting + * @param string $property + * @param string $prefix + * @param string $shortPrefix + * @return type + */ + protected function getEnvValue(string $property, string $prefix, string $shortPrefix) + { + if (($value = getenv("{$shortPrefix}.{$property}")) !== false) + { + return $value; + } + elseif (($value = getenv("{$prefix}.{$property}")) !== false) + { + return $value; + } + elseif (($value = getenv($property)) !== false && $property != 'path') + { + return $value; + } + + return null; + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Config/DotEnv.php b/ci-4.0-dev/system/Config/DotEnv.php new file mode 100644 index 000000000..a95c7645f --- /dev/null +++ b/ci-4.0-dev/system/Config/DotEnv.php @@ -0,0 +1,297 @@ +path = rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file; + } + + //-------------------------------------------------------------------- + + /** + * The main entry point, will load the .env file and process it + * so that we end up with all settings in the PHP environment vars + * (i.e. getenv(), $_ENV, and $_SERVER) + */ + public function load() + { + // We don't want to enforce the presence of a .env file, + // they should be optional. + if ( ! is_file($this->path)) + { + return false; + } + + // Ensure file is readable + if ( ! is_readable($this->path)) + { + throw new \InvalidArgumentException("The .env file is not readable: {$this->path}"); + } + + $lines = file($this->path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + + foreach ($lines as $line) + { + // Is it a comment? + if (strpos(trim($line), '#') === 0) + { + continue; + } + + // If there is an equal sign, then we know we + // are assigning a variable. + if (strpos($line, '=') !== false) + { + $this->setVariable($line); + } + } + } + + //-------------------------------------------------------------------- + + /** + * Sets the variable into the environment. Will parse the string + * first to look for {name}={value} pattern, ensure that nested + * variables are handled, and strip it of single and double quotes. + * + * @param string $name + * @param string $value + */ + protected function setVariable(string $name, string $value = '') + { + list($name, $value) = $this->normaliseVariable($name, $value); + + putenv("$name=$value"); + $_ENV[$name] = $value; + $_SERVER[$name] = $value; + } + + //-------------------------------------------------------------------- + + /** + * Parses for assignment, cleans the $name and $value, and ensures + * that nested variables are handled. + * + * @param string $name + * @param string $value + * @return array + */ + public function normaliseVariable(string $name, string $value = ''): array + { + // Split our compound string into it's parts. + if (strpos($name, '=') !== false) + { + list($name, $value) = explode('=', $name, 2); + } + + $name = trim($name); + $value = trim($value); + + // Sanitize the name + $name = str_replace(['export', '\'', '"'], '', $name); + + // Sanitize the value + $value = $this->sanitizeValue($value); + + $value = $this->resolveNestedVariables($value); + + return [$name, $value]; + } + + //-------------------------------------------------------------------- + + /** + * Strips quotes from the environment variable value. + * + * This was borrowed from the excellent phpdotenv with very few changes. + * https://github.com/vlucas/phpdotenv + * + * @param string $value + * + * @return string + * @throws \InvalidArgumentException + */ + protected function sanitizeValue(string $value): string + { + if ( ! $value) + { + return $value; + } + + // Does it begin with a quote? + if (strpbrk($value[0], '"\'') !== false) + { + // value starts with a quote + $quote = $value[0]; + $regexPattern = sprintf( + '/^ + %1$s # match a quote at the start of the value + ( # capturing sub-pattern used + (?: # we do not need to capture this + [^%1$s\\\\] # any character other than a quote or backslash + |\\\\\\\\ # or two backslashes together + |\\\\%1$s # or an escaped quote e.g \" + )* # as many characters that match the previous rules + ) # end of the capturing sub-pattern + %1$s # and the closing quote + .*$ # and discard any string after the closing quote + /mx', + $quote + ); + $value = preg_replace($regexPattern, '$1', $value); + $value = str_replace("\\$quote", $quote, $value); + $value = str_replace('\\\\', '\\', $value); + } + else + { + $parts = explode(' #', $value, 2); + + $value = trim($parts[0]); + + // Unquoted values cannot contain whitespace + if (preg_match('/\s+/', $value) > 0) + { + throw new \InvalidArgumentException('.env values containing spaces must be surrounded by quotes.'); + } + } + + return $value; + } + + //-------------------------------------------------------------------- + + /** + * Resolve the nested variables. + * + * Look for ${varname} patterns in the variable value and replace with an existing + * environment variable. + * + * This was borrowed from the excellent phpdotenv with very few changes. + * https://github.com/vlucas/phpdotenv + * + * @param $value + * + * @return string + */ + protected function resolveNestedVariables(string $value): string + { + if (strpos($value, '$') !== false) + { + $loader = $this; + + $value = preg_replace_callback( + '/\${([a-zA-Z0-9_]+)}/', + function ($matchedPatterns) use ($loader) + { + $nestedVariable = $loader->getVariable($matchedPatterns[1]); + + if (is_null($nestedVariable)) + { + return $matchedPatterns[0]; + } + else + { + return $nestedVariable; + } + }, + $value + ); + } + + return $value; + } + + //-------------------------------------------------------------------- + + /** + * Search the different places for environment variables and return first value found. + * + * This was borrowed from the excellent phpdotenv with very few changes. + * https://github.com/vlucas/phpdotenv + * + * @param string $name + * + * @return string|null + */ + protected function getVariable(string $name) + { + switch (true) + { + case array_key_exists($name, $_ENV): + return $_ENV[$name]; + break; + case array_key_exists($name, $_SERVER): + return $_SERVER[$name]; + break; + default: + $value = getenv($name); + + // switch getenv default to null + return $value === false ? null : $value; + } + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Config/ForeignCharacters.php b/ci-4.0-dev/system/Config/ForeignCharacters.php new file mode 100644 index 000000000..f4a559b34 --- /dev/null +++ b/ci-4.0-dev/system/Config/ForeignCharacters.php @@ -0,0 +1,141 @@ + 'ae', + '/ö|Å“/' => 'oe', + '/ü/' => 'ue', + '/Ä/' => 'Ae', + '/Ü/' => 'Ue', + '/Ö/' => 'Oe', + '/À|Ã|Â|Ã|Ä|Ã…|Ǻ|Ä€|Ä‚|Ä„|Ç|Α|Ά|Ả|Ạ|Ầ|Ẫ|Ẩ|Ậ|Ằ|Ắ|Ẵ|Ẳ|Ặ|Ð/' => 'A', + '/à|á|â|ã|Ã¥|Ç»|Ä|ă|Ä…|ÇŽ|ª|α|ά|ả|ạ|ầ|ấ|ẫ|ẩ|ậ|ằ|ắ|ẵ|ẳ|ặ|а/' => 'a', + '/Б/' => 'B', + '/б/' => 'b', + '/Ç|Ć|Ĉ|ÄŠ|ÄŒ/' => 'C', + '/ç|ć|ĉ|Ä‹|Ä/' => 'c', + '/Д/' => 'D', + '/д/' => 'd', + '/Ã|ÄŽ|Ä|Δ/' => 'Dj', + '/ð|Ä|Ä‘|δ/' => 'dj', + '/È|É|Ê|Ë|Ä’|Ä”|Ä–|Ę|Äš|Ε|Έ|Ẽ|Ẻ|Ẹ|Ề|Ế|Ễ|Ể|Ệ|Е|Э/' => 'E', + '/è|é|ê|ë|Ä“|Ä•|Ä—|Ä™|Ä›|έ|ε|ẽ|ẻ|ẹ|á»|ế|á»…|ể|ệ|е|Ñ/' => 'e', + '/Ф/' => 'F', + '/Ñ„/' => 'f', + '/Äœ|Äž|Ä |Ä¢|Γ|Г|Ò/' => 'G', + '/Ä|ÄŸ|Ä¡|Ä£|γ|г|Ò‘/' => 'g', + '/Ĥ|Ħ/' => 'H', + '/Ä¥|ħ/' => 'h', + '/ÃŒ|Ã|ÃŽ|Ã|Ĩ|Ī|Ĭ|Ç|Ä®|İ|Η|Ή|Ί|Ι|Ϊ|Ỉ|Ị|И|Ы/' => 'I', + '/ì|í|î|ï|Ä©|Ä«|Ä­|Ç|į|ı|η|ή|ί|ι|ÏŠ|ỉ|ị|и|Ñ‹|Ñ—/' => 'i', + '/Ä´/' => 'J', + '/ĵ/' => 'j', + '/Ķ|Κ|К/' => 'K', + '/Ä·|κ|к/' => 'k', + '/Ĺ|Ä»|Ľ|Ä¿|Å|Λ|Л/' => 'L', + '/ĺ|ļ|ľ|Å€|Å‚|λ|л/' => 'l', + '/М/' => 'M', + '/м/' => 'm', + '/Ñ|Ń|Å…|Ň|Î|Ð/' => 'N', + '/ñ|Å„|ņ|ň|ʼn|ν|н/' => 'n', + '/Ã’|Ó|Ô|Õ|ÅŒ|ÅŽ|Ç‘|Å|Æ |Ø|Ǿ|Ο|ÎŒ|Ω|Î|Ỏ|Ọ|á»’|á»|á»–|á»”|Ộ|Ờ|Ớ|á» |Ở|Ợ|О/' => 'O', + '/ò|ó|ô|õ|Å|Å|Ç’|Å‘|Æ¡|ø|Ç¿|º|ο|ÏŒ|ω|ÏŽ|á»|á»|ồ|ố|á»—|ổ|á»™|á»|á»›|ỡ|ở|ợ|о/' => 'o', + '/П/' => 'P', + '/п/' => 'p', + '/Å”|Å–|Ř|Ρ|Р/' => 'R', + '/Å•|Å—|Å™|Ï|Ñ€/' => 'r', + '/Åš|Åœ|Åž|Ș|Å |Σ|С/' => 'S', + '/Å›|Å|ÅŸ|È™|Å¡|Å¿|σ|Ï‚|Ñ/' => 's', + '/Èš|Å¢|Ť|Ŧ|Ï„|Т/' => 'T', + '/È›|Å£|Å¥|ŧ|Ñ‚/' => 't', + '/Ù|Ú|Û|Ũ|Ū|Ŭ|Å®|Ű|Ų|Ư|Ç“|Ç•|Ç—|Ç™|Ç›|Ũ|Ủ|Ụ|Ừ|Ứ|á»®|Ử|á»°|У/' => 'U', + '/ù|ú|û|Å©|Å«|Å­|ů|ű|ų|ư|Ç”|Ç–|ǘ|Çš|Çœ|Ï…|Ï|Ï‹|á»§|ụ|ừ|ứ|ữ|á»­|á»±|у/' => 'u', + '/Ã|Ÿ|Ŷ|Î¥|ÎŽ|Ϋ|Ỳ|Ỹ|á»¶|á»´|Й/' => 'Y', + '/ý|ÿ|Å·|ỳ|ỹ|á»·|ỵ|й/' => 'y', + '/Ð’/' => 'V', + '/в/' => 'v', + '/Å´/' => 'W', + '/ŵ/' => 'w', + '/Ź|Å»|Ž|Ζ|З/' => 'Z', + '/ź|ż|ž|ζ|з/' => 'z', + '/Æ|Ǽ/' => 'AE', + '/ß/' => 'ss', + '/IJ/' => 'IJ', + '/ij/' => 'ij', + '/Å’/' => 'OE', + '/Æ’/' => 'f', + '/ξ/' => 'ks', + '/Ï€/' => 'p', + '/β/' => 'v', + '/μ/' => 'm', + '/ψ/' => 'ps', + '/Ð/' => 'Yo', + '/Ñ‘/' => 'yo', + '/Є/' => 'Ye', + '/Ñ”/' => 'ye', + '/Ї/' => 'Yi', + '/Ж/' => 'Zh', + '/ж/' => 'zh', + '/Ð¥/' => 'Kh', + '/Ñ…/' => 'kh', + '/Ц/' => 'Ts', + '/ц/' => 'ts', + '/Ч/' => 'Ch', + '/ч/' => 'ch', + '/Ш/' => 'Sh', + '/ш/' => 'sh', + '/Щ/' => 'Shch', + '/щ/' => 'shch', + '/Ъ|ÑŠ|Ь|ÑŒ/' => '', + '/Ю/' => 'Yu', + '/ÑŽ/' => 'yu', + '/Я/' => 'Ya', + '/Ñ/' => 'ya' + ]; + +} diff --git a/ci-4.0-dev/system/Config/Routes.php b/ci-4.0-dev/system/Config/Routes.php new file mode 100644 index 000000000..afe10a4be --- /dev/null +++ b/ci-4.0-dev/system/Config/Routes.php @@ -0,0 +1,52 @@ +cli('migrations/(:segment)/(:segment)', '\CodeIgniter\Commands\MigrationsCommand::$1/$2'); +$routes->cli('migrations/(:segment)', '\CodeIgniter\Commands\MigrationsCommand::$1'); +$routes->cli('migrations', '\CodeIgniter\Commands\MigrationsCommand::index'); diff --git a/ci-4.0-dev/system/Config/Services.php b/ci-4.0-dev/system/Config/Services.php new file mode 100644 index 000000000..895cfe82d --- /dev/null +++ b/ci-4.0-dev/system/Config/Services.php @@ -0,0 +1,617 @@ +getLocale(); + + return new \CodeIgniter\Language\Language($locale); + } + + //-------------------------------------------------------------------- + + /** + * The file locator provides utility methods for looking for non-classes + * within namespaced folders, as well as convenience methods for + * loading 'helpers', and 'libraries'. + */ + public static function locator($getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('locator'); + } + + return new \CodeIgniter\Autoloader\FileLocator(new \Config\Autoload()); + } + + //-------------------------------------------------------------------- + + /** + * The Logger class is a PSR-3 compatible Logging class that supports + * multiple handlers that process the actual logging. + */ + public static function logger($getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('logger'); + } + + return new \CodeIgniter\Log\Logger(new \Config\Logger()); + } + + //-------------------------------------------------------------------- + + public static function migrations(BaseConfig $config = null, ConnectionInterface $db = null, bool $getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('migrations', $config, $db); + } + + $config = empty($config) ? new \Config\Migrations() : $config; + + return new MigrationRunner($config, $db); + } + + //-------------------------------------------------------------------- + + + /** + * The Negotiate class provides the content negotiation features for + * working the request to determine correct language, encoding, charset, + * and more. + */ + public static function negotiator(\CodeIgniter\HTTP\RequestInterface $request=null, $getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('negotiator', $request); + } + + if (is_null($request)) + { + $request = self::request(); + } + + return new \CodeIgniter\HTTP\Negotiate($request); + } + + //-------------------------------------------------------------------- + + public static function pager($config = null, RendererInterface $view = null, $getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('pager', $config, $view); + } + + if (empty($config)) + { + $config = new \Config\Pager(); + } + + if (! $view instanceof RendererInterface) + { + $view = self::renderer(); + } + + return new \CodeIgniter\Pager\Pager($config, $view); + } + + //-------------------------------------------------------------------- + + /** + * The Renderer class is the class that actually displays a file to the user. + * The default View class within CodeIgniter is intentionally simple, but this + * service could easily be replaced by a template engine if the user needed to. + */ + public static function renderer($viewPath = APPPATH.'Views/', $config = null, $getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('renderer', $viewPath, $config); + } + + if (is_null($config)) + { + $config = new \Config\View(); + } + + return new \CodeIgniter\View\View($config, $viewPath, self::locator(true), CI_DEBUG, self::logger(true)); + } + + //-------------------------------------------------------------------- + + /** + * The Request class models an HTTP request. + */ + public static function request(\Config\App $config = null, $getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('request', $config); + } + + if (! is_object($config)) + { + $config = new \Config\App(); + } + + return new \CodeIgniter\HTTP\IncomingRequest( + $config, + new \CodeIgniter\HTTP\URI() + ); + } + + //-------------------------------------------------------------------- + + /** + * The Response class models an HTTP response. + */ + public static function response(\Config\App $config = null, $getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('response', $config); + } + + if (! is_object($config)) + { + $config = new \Config\App(); + } + + return new \CodeIgniter\HTTP\Response($config); + } + + //-------------------------------------------------------------------- + + /** + * The Routes service is a class that allows for easily building + * a collection of routes. + */ + public static function routes($getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('routes'); + } + + return new \CodeIgniter\Router\RouteCollection(); + } + + //-------------------------------------------------------------------- + + /** + * The Router class uses a RouteCollection's array of routes, and determines + * the correct Controller and Method to execute. + */ + public static function router(\CodeIgniter\Router\RouteCollectionInterface $routes = null, $getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('router', $routes); + } + + if (empty($routes)) + { + $routes = self::routes(true); + } + + return new \CodeIgniter\Router\Router($routes); + } + + //-------------------------------------------------------------------- + + /** + * The Security class provides a few handy tools for keeping the site + * secure, most notably the CSRF protection tools. + */ + public static function security(\Config\App $config = null, $getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('security', $config); + } + + if (! is_object($config)) + { + $config = new \Config\App(); + } + + return new \CodeIgniter\Security\Security($config); + } + + //-------------------------------------------------------------------- + + /** + * @param App|null $config + * @param bool $getShared + * + * @return \CodeIgniter\Session\Session + */ + public static function session(\Config\App $config = null, $getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('session', $config); + } + + if (! is_object($config)) + { + $config = new \Config\App(); + } + + $logger = self::logger(true); + + $driverName = $config->sessionDriver; + $driver = new $driverName($config); + $driver->setLogger($logger); + + $session = new \CodeIgniter\Session\Session($driver, $config); + $session->setLogger($logger); + + return $session; + } + + //-------------------------------------------------------------------- + + /** + * The Throttler class provides a simple method for implementing + * rate limiting in your applications. + */ + public static function throttler($getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('throttler'); + } + + return new \CodeIgniter\Throttle\Throttler(self::cache()); + } + + //-------------------------------------------------------------------- + + /** + * The Timer class provides a simple way to Benchmark portions of your + * application. + */ + public static function timer($getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('timer'); + } + + return new \CodeIgniter\Debug\Timer(); + } + + //-------------------------------------------------------------------- + + public static function toolbar(\Config\App $config = null, $getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('toolbar', $config); + } + + if (! is_object($config)) + { + $config = new \Config\App(); + } + + return new \CodeIgniter\Debug\Toolbar($config); + } + + //-------------------------------------------------------------------- + + /** + * The URI class provides a way to model and manipulate URIs. + */ + public static function uri($uri = null, $getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('uri', $uri); + } + + return new \CodeIgniter\HTTP\URI($uri); + } + + //-------------------------------------------------------------------- + + /** + * The Validation class provides tools for validating input data. + */ + public static function validation(\Config\Validation $config = null, $getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('validation', $config); + } + + if (is_null($config)) + { + $config = new \Config\Validation(); + } + + return new \CodeIgniter\Validation\Validation($config, self::renderer()); + } + + //-------------------------------------------------------------------- + + /** + * View cells are intended to let you insert HTML into view + * that has been generated by any callable in the system. + */ + public static function viewcell($getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('viewcell'); + } + + return new \CodeIgniter\View\Cell(self::cache()); + } + + //-------------------------------------------------------------------- + + /** + * The Typography class provides a way to format text in semantically relevant ways. + */ + public static function typography($getShared = true) + { + if ($getShared) + { + return self::getSharedInstance('typography'); + } + + return new \CodeIgniter\Typography\Typography(); + } + + //-------------------------------------------------------------------- + + //-------------------------------------------------------------------- + // Utility Methods - DO NOT EDIT + //-------------------------------------------------------------------- + + /** + * Returns a shared instance of any of the class' services. + * + * $key must be a name matching a service. + * + * @param string $key + */ + protected static function getSharedInstance(string $key, ...$params) + { + if (! isset(static::$instances[$key])) + { + // Make sure $getShared is false + array_push($params, false); + + static::$instances[$key] = static::$key(...$params); + } + + return static::$instances[$key]; + } + + //-------------------------------------------------------------------- + + /** + * Provides the ability to perform case-insensitive calling of service + * names. + * + * @param string $name + * @param array $arguments + */ + public static function __callStatic(string $name, array $arguments) + { + $name = strtolower($name); + + if (method_exists(__CLASS__, $name)) + { + return Services::$name(...$arguments); + } + } + + //-------------------------------------------------------------------- + + +} diff --git a/ci-4.0-dev/system/Controller.php b/ci-4.0-dev/system/Controller.php new file mode 100644 index 000000000..d0abd90b1 --- /dev/null +++ b/ci-4.0-dev/system/Controller.php @@ -0,0 +1,196 @@ +request = $request; + + $this->response = $response; + + $this->logger = is_null($logger) ? Services::logger(true) : $logger; + + $this->logger->info('Controller "'.get_class($this).'" loaded.'); + + if ($this->forceHTTPS > 0) + { + $this->forceHTTPS($this->forceHTTPS); + } + + $this->loadHelpers(); + } + + //-------------------------------------------------------------------- + + /** + * A convenience method to use when you need to ensure that a single + * method is reached only via HTTPS. If it isn't, then a redirect + * will happen back to this method and HSTS header will be sent + * to have modern browsers transform requests automatically. + * + * @param int $duration The number of seconds this link should be + * considered secure for. Only with HSTS header. + * Default value is 1 year. + */ + public function forceHTTPS(int $duration = 31536000) + { + force_https($duration, $this->request, $this->response); + } + + //-------------------------------------------------------------------- + + /** + * Provides a simple way to tie into the main CodeIgniter class + * and tell it how long to cache the current page for. + * + * @param int $time + */ + public function cachePage(int $time) + { + CodeIgniter::cache($time); + } + + //-------------------------------------------------------------------- + + /** + * Handles "auto-loading" helper files. + */ + protected function loadHelpers() + { + if (empty($this->helpers)) return; + + foreach ($this->helpers as $helper) + { + helper($helper); + } + } + + //-------------------------------------------------------------------- + + /** + * A shortcut to performing validation on $_POST input. If validation + * is not successful, a $errors property will be set on this class. + * + * @param \CodeIgniter\HTTP\RequestInterface $request + * @param $rules + * @param array|null $messages + * + * @return bool + */ + public function validate(RequestInterface $request, $rules, array $messages = null): bool + { + $this->validator = Services::validation(); + + $success = $this->validator->withRequest($request) + ->setRules($rules, $messages) + ->run(); + + return $success; + } + + //-------------------------------------------------------------------- + + +} diff --git a/ci-4.0-dev/system/Database/BaseBuilder.php b/ci-4.0-dev/system/Database/BaseBuilder.php new file mode 100644 index 000000000..7bc0b0aa8 --- /dev/null +++ b/ci-4.0-dev/system/Database/BaseBuilder.php @@ -0,0 +1,2878 @@ +db = $db; + + $this->from($tableName); + + if (count($options)) + { + foreach ($options as $key => $value) + { + $this->$key = $value; + } + } + } + + //-------------------------------------------------------------------- + + /** + * Returns an array of bind values and their + * named parameters for binding in the Query object later. + * + * @return array + */ + public function getBinds(): array + { + return $this->binds; + } + + //-------------------------------------------------------------------- + + /** + * Select + * + * Generates the SELECT portion of the query + * + * @param string|array + * @param mixed + * + * @return BaseBuilder + */ + public function select($select = '*', $escape = null) + { + if (is_string($select)) + { + $select = explode(',', $select); + } + + // If the escape value was not set, we will base it on the global setting + is_bool($escape) || $escape = $this->db->protectIdentifiers; + + foreach ($select as $val) + { + $val = trim($val); + + if ($val !== '') + { + $this->QBSelect[] = $val; + $this->QBNoEscape[] = $escape; + } + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Select Max + * + * Generates a SELECT MAX(field) portion of a query + * + * @param string the field + * @param string an alias + * + * @return BaseBuilder + */ + public function selectMax($select = '', $alias = '') + { + return $this->maxMinAvgSum($select, $alias, 'MAX'); + } + + //-------------------------------------------------------------------- + + /** + * Select Min + * + * Generates a SELECT MIN(field) portion of a query + * + * @param string the field + * @param string an alias + * + * @return BaseBuilder + */ + public function selectMin($select = '', $alias = '') + { + return $this->maxMinAvgSum($select, $alias, 'MIN'); + } + + //-------------------------------------------------------------------- + + /** + * Select Average + * + * Generates a SELECT AVG(field) portion of a query + * + * @param string the field + * @param string an alias + * + * @return BaseBuilder + */ + public function selectAvg($select = '', $alias = '') + { + return $this->maxMinAvgSum($select, $alias, 'AVG'); + } + + //-------------------------------------------------------------------- + + /** + * Select Sum + * + * Generates a SELECT SUM(field) portion of a query + * + * @param string the field + * @param string an alias + * + * @return BaseBuilder + */ + public function selectSum($select = '', $alias = '') + { + return $this->maxMinAvgSum($select, $alias, 'SUM'); + } + + //-------------------------------------------------------------------- + + /** + * SELECT [MAX|MIN|AVG|SUM]() + * + * @used-by selectMax() + * @used-by selectMin() + * @used-by selectAvg() + * @used-by selectSum() + * + * @param string $select Field name + * @param string $alias + * @param string $type + * + * @return BaseBuilder + */ + protected function maxMinAvgSum($select = '', $alias = '', $type = 'MAX') + { + if ( ! is_string($select) || $select === '') + { + throw new DatabaseException('The query you submitted is not valid.'); + } + + $type = strtoupper($type); + + if ( ! in_array($type, ['MAX', 'MIN', 'AVG', 'SUM'])) + { + throw new DatabaseException('Invalid function type: '.$type); + } + + if ($alias === '') + { + $alias = $this->createAliasFromTable(trim($select)); + } + + $sql = $type.'('.$this->db->protectIdentifiers(trim($select)).') AS '.$this->db->escapeIdentifiers(trim($alias)); + + $this->QBSelect[] = $sql; + $this->QBNoEscape[] = null; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Determines the alias name based on the table + * + * @param string $item + * + * @return string + */ + protected function createAliasFromTable($item) + { + if (strpos($item, '.') !== false) + { + $item = explode('.', $item); + + return end($item); + } + + return $item; + } + + //-------------------------------------------------------------------- + + /** + * DISTINCT + * + * Sets a flag which tells the query string compiler to add DISTINCT + * + * @param bool $val + * + * @return BaseBuilder + */ + public function distinct($val = true) + { + $this->QBDistinct = is_bool($val) ? $val : true; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * From + * + * Generates the FROM portion of the query + * + * @param mixed $from can be a string or array + * @param bool $overwrite Should we remove the first table existing? + * + * @return BaseBuilder + */ + public function from($from, $overwrite = false) + { + if ($overwrite === true) + { + $this->QBFrom = []; + $this->QBAliasedTables = []; + } + + foreach ((array)$from as $val) + { + if (strpos($val, ',') !== false) + { + foreach (explode(',', $val) as $v) + { + $v = trim($v); + $this->trackAliases($v); + + $this->QBFrom[] = $v = $this->db->protectIdentifiers($v, true, null, false); + } + } + else + { + $val = trim($val); + + // Extract any aliases that might exist. We use this information + // in the protectIdentifiers to know whether to add a table prefix + $this->trackAliases($val); + + $this->QBFrom[] = $this->db->protectIdentifiers($val, true, null, false); + } + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * JOIN + * + * Generates the JOIN portion of the query + * + * @param string + * @param string the join condition + * @param string the type of join + * @param string whether not to try to escape identifiers + * + * @return BaseBuilder + */ + public function join($table, $cond, $type = '', $escape = null) + { + if ($type !== '') + { + $type = strtoupper(trim($type)); + + if ( ! in_array($type, ['LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER'], true)) + { + $type = ''; + } + else + { + $type .= ' '; + } + } + + // Extract any aliases that might exist. We use this information + // in the protectIdentifiers to know whether to add a table prefix + $this->trackAliases($table); + + is_bool($escape) || $escape = $this->db->protectIdentifiers; + + if ( ! $this->hasOperator($cond)) + { + $cond = ' USING ('.($escape ? $this->db->escapeIdentifiers($cond) : $cond).')'; + } + elseif ($escape === false) + { + $cond = ' ON '.$cond; + } + else + { + // Split multiple conditions + if (preg_match_all('/\sAND\s|\sOR\s/i', $cond, $joints, PREG_OFFSET_CAPTURE)) + { + $conditions = []; + $joints = $joints[0]; + array_unshift($joints, ['', 0]); + + for ($i = count($joints) - 1, $pos = strlen($cond); $i >= 0; $i--) + { + $joints[$i][1] += strlen($joints[$i][0]); // offset + $conditions[$i] = substr($cond, $joints[$i][1], $pos - $joints[$i][1]); + $pos = $joints[$i][1] - strlen($joints[$i][0]); + $joints[$i] = $joints[$i][0]; + } + } + else + { + $conditions = [$cond]; + $joints = ['']; + } + + $cond = ' ON '; + for ($i = 0, $c = count($conditions); $i < $c; $i++) + { + $operator = $this->getOperator($conditions[$i]); + $cond .= $joints[$i]; + $cond .= preg_match("/(\(*)?([\[\]\w\.'-]+)".preg_quote($operator)."(.*)/i", $conditions[$i], $match) + ? $match[1].$this->db->protectIdentifiers($match[2]).$operator.$this->db->protectIdentifiers($match[3]) + : $conditions[$i]; + } + } + + // Do we want to escape the table name? + if ($escape === true) + { + $table = $this->db->protectIdentifiers($table, true, null, false); + } + + // Assemble the JOIN statement + $this->QBJoin[] = $join = $type.'JOIN '.$table.$cond; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * WHERE + * + * Generates the WHERE portion of the query. + * Separates multiple calls with 'AND'. + * + * @param mixed + * @param mixed + * @param bool + * + * @return BaseBuilder + */ + public function where($key, $value = null, $escape = null) + { + return $this->whereHaving('QBWhere', $key, $value, 'AND ', $escape); + } + + //-------------------------------------------------------------------- + + /** + * OR WHERE + * + * Generates the WHERE portion of the query. + * Separates multiple calls with 'OR'. + * + * @param mixed + * @param mixed + * @param bool + * + * @return BaseBuilder + */ + public function orWhere($key, $value = null, $escape = null) + { + return $this->whereHaving('QBWhere', $key, $value, 'OR ', $escape); + } + + //-------------------------------------------------------------------- + + /** + * WHERE, HAVING + * + * @used-by where() + * @used-by orWhere() + * @used-by having() + * @used-by orHaving() + * + * @param string $qb_key 'QBWhere' or 'QBHaving' + * @param mixed $key + * @param mixed $value + * @param string $type + * @param bool $escape + * + * @return BaseBuilder + */ + protected function whereHaving($qb_key, $key, $value = null, $type = 'AND ', $escape = null) + { + if ( ! is_array($key)) + { + $key = [$key => $value]; + } + + // If the escape value was not set will base it on the global setting + is_bool($escape) || $escape = $this->db->protectIdentifiers; + + foreach ($key as $k => $v) + { + $prefix = (count($this->$qb_key) === 0) + ? $this->groupGetType('') + : $this->groupGetType($type); + + if ($v !== null) + { + $op = $this->getOperator($k); + $k = trim(str_replace($op, '', $k)); + + $bind = $this->setBind($k, $v); + + if (empty($op)) + { + $k .= ' ='; + } + else + { + $k .= $op; + } + } + elseif ( ! $this->hasOperator($k)) + { + // value appears not to have been set, assign the test to IS NULL + $k .= ' IS NULL'; + } + elseif (preg_match('/\s*(!?=|<>|IS(?:\s+NOT)?)\s*$/i', $k, $match, PREG_OFFSET_CAPTURE)) + { + $k = substr($k, 0, $match[0][1]).($match[1][0] === '=' ? ' IS NULL' : ' IS NOT NULL'); + } + + $v = ! is_null($v) ? ' :'.$bind : $v; + + $this->{$qb_key}[] = ['condition' => $prefix.$k.$v, 'escape' => $escape]; + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * WHERE IN + * + * Generates a WHERE field IN('item', 'item') SQL query, + * joined with 'AND' if appropriate. + * + * @param string $key The field to search + * @param array $values The values searched on + * @param bool $escape + * + * @return BaseBuilder + */ + public function whereIn($key = null, $values = null, $escape = null) + { + return $this->_whereIn($key, $values, false, 'AND ', $escape); + } + + //-------------------------------------------------------------------- + + /** + * OR WHERE IN + * + * Generates a WHERE field IN('item', 'item') SQL query, + * joined with 'OR' if appropriate. + * + * @param string $key The field to search + * @param array $values The values searched on + * @param bool $escape + * + * @return BaseBuilder + */ + public function orWhereIn($key = null, $values = null, $escape = null) + { + return $this->_whereIn($key, $values, false, 'OR ', $escape); + } + + //-------------------------------------------------------------------- + + /** + * WHERE NOT IN + * + * Generates a WHERE field NOT IN('item', 'item') SQL query, + * joined with 'AND' if appropriate. + * + * @param string $key The field to search + * @param array $values The values searched on + * @param bool $escape + * + * @return BaseBuilder + */ + public function whereNotIn($key = null, $values = null, $escape = null) + { + return $this->_whereIn($key, $values, true, 'AND ', $escape); + } + + //-------------------------------------------------------------------- + + /** + * OR WHERE NOT IN + * + * Generates a WHERE field NOT IN('item', 'item') SQL query, + * joined with 'OR' if appropriate. + * + * @param string $key The field to search + * @param array $values The values searched on + * @param bool $escape + * + * @return BaseBuilder + */ + public function orWhereNotIn($key = null, $values = null, $escape = null) + { + return $this->_whereIn($key, $values, true, 'OR ', $escape); + } + + //-------------------------------------------------------------------- + + /** + * Internal WHERE IN + * + * @used-by WhereIn() + * @used-by orWhereIn() + * @used-by whereNotIn() + * @used-by orWhereNotIn() + * + * @param string $key The field to search + * @param array $values The values searched on + * @param bool $not If the statement would be IN or NOT IN + * @param string $type + * @param bool $escape + * + * @return BaseBuilder + */ + protected function _whereIn($key = null, $values = null, $not = false, $type = 'AND ', $escape = null) + { + if ($key === null OR $values === null) + { + return $this; + } + + if ( ! is_array($values)) + { + $values = [$values]; + } + + is_bool($escape) || $escape = $this->db->protectIdentifiers; + + $ok = $key; + + if ($escape === true) + { + $key = $this->db->protectIdentifiers($key); + } + + $not = ($not) ? ' NOT' : ''; + + $where_in = array_values($values); + $this->binds[$ok] = $where_in; + + $prefix = (count($this->QBWhere) === 0) + ? $this->groupGetType('') + : $this->groupGetType($type); + + $where_in = [ + 'condition' => $prefix.$key.$not.' IN :'.$ok, + 'escape' => false, + ]; + + $this->QBWhere[] = $where_in; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * LIKE + * + * Generates a %LIKE% portion of the query. + * Separates multiple calls with 'AND'. + * + * @param mixed $field + * @param string $match + * @param string $side + * @param bool $escape + * @param bool $insensitiveSearch IF true, will force a case-insensitive search + * + * @return BaseBuilder + */ + public function like($field, $match = '', $side = 'both', $escape = null, $insensitiveSearch = false) + { + return $this->_like($field, $match, 'AND ', $side, '', $escape, $insensitiveSearch); + } + + //-------------------------------------------------------------------- + + /** + * NOT LIKE + * + * Generates a NOT LIKE portion of the query. + * Separates multiple calls with 'AND'. + * + * @param mixed $field + * @param string $match + * @param string $side + * @param bool $escape + * @param bool $insensitiveSearch IF true, will force a case-insensitive search + * + * @return BaseBuilder + */ + public function notLike($field, $match = '', $side = 'both', $escape = null, $insensitiveSearch = false) + { + return $this->_like($field, $match, 'AND ', $side, 'NOT', $escape, $insensitiveSearch); + } + + //-------------------------------------------------------------------- + + /** + * OR LIKE + * + * Generates a %LIKE% portion of the query. + * Separates multiple calls with 'OR'. + * + * @param mixed $field + * @param string $match + * @param string $side + * @param bool $escape + * @param bool $insensitiveSearch IF true, will force a case-insensitive search + * + * @return BaseBuilder + */ + public function orLike($field, $match = '', $side = 'both', $escape = null, $insensitiveSearch = false) + { + return $this->_like($field, $match, 'OR ', $side, '', $escape, $insensitiveSearch); + } + + //-------------------------------------------------------------------- + + /** + * OR NOT LIKE + * + * Generates a NOT LIKE portion of the query. + * Separates multiple calls with 'OR'. + * + * @param mixed $field + * @param string $match + * @param string $side + * @param bool $escape + * @param bool $insensitiveSearch IF true, will force a case-insensitive search + * + * @return BaseBuilder + */ + public function orNotLike($field, $match = '', $side = 'both', $escape = null, $insensitiveSearch = false) + { + return $this->_like($field, $match, 'OR ', $side, 'NOT', $escape, $insensitiveSearch); + } + + //-------------------------------------------------------------------- + + /** + * Internal LIKE + * + * @used-by like() + * @used-by orLike() + * @used-by notLike() + * @used-by orNotLike() + * + * @param mixed $field + * @param string $match + * @param string $type + * @param string $side + * @param string $not + * @param bool $escape + * @param bool $insensitiveSearch IF true, will force a case-insensitive search + * + * @return BaseBuilder + */ + protected function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '', $escape = null, $insensitiveSearch = false) + { + if ( ! is_array($field)) + { + $field = [$field => $match]; + } + + $escape = is_bool($escape) ? $escape : $this->db->protectIdentifiers; + + // lowercase $side in case somebody writes e.g. 'BEFORE' instead of 'before' (doh) + $side = strtolower($side); + + foreach ($field as $k => $v) + { + $prefix = (count($this->QBWhere) === 0) + ? $this->groupGetType('') : $this->groupGetType($type); + + if ($insensitiveSearch === true) + { + $v = strtolower($v); + } + + if ($side === 'none') + { + $bind = $this->setBind($k, $v); + } + elseif ($side === 'before') + { + $bind = $this->setBind($k, "%$v"); + } + elseif ($side === 'after') + { + $bind = $this->setBind($k, "$v%"); + } + else + { + $bind = $this->setBind($k, "%$v%"); + } + + $like_statement = $this->_like_statement($prefix, $k, $not, $bind, $insensitiveSearch); + + // some platforms require an escape sequence definition for LIKE wildcards + if ($escape === true && $this->db->likeEscapeStr !== '') + { + $like_statement .= sprintf($this->db->likeEscapeStr, $this->db->likeEscapeChar); + } + + $this->QBWhere[] = ['condition' => $like_statement, 'escape' => $escape]; + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Platform independent LIKE statement builder. + * + * @param string|null $prefix + * @param string $column + * @param string|null $not + * @param string $bind + * @param bool $insensitiveSearch + * + * @return string $like_statement + */ + public function _like_statement(string $prefix=null, string $column, string $not = null, string $bind, bool $insensitiveSearch=false): string + { + $like_statement = "{$prefix} {$column} {$not} LIKE :{$bind}"; + + if ($insensitiveSearch === true) + { + $like_statement = "{$prefix} LOWER({$column}) {$not} LIKE :{$bind}"; + } + + return $like_statement; + } + + //-------------------------------------------------------------------- + + /** + * Starts a query group. + * + * @param string $not (Internal use only) + * @param string $type (Internal use only) + * + * @return BaseBuilder + */ + public function groupStart($not = '', $type = 'AND ') + { + $type = $this->groupGetType($type); + + $this->QBWhereGroupStarted = true; + $prefix = count($this->QBWhere) === 0 ? '' + : $type; + $where = [ + 'condition' => $prefix.$not.str_repeat(' ', ++$this->QBWhereGroupCount).' (', + 'escape' => false, + ]; + + $this->QBWhere[] = $where; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Starts a query group, but ORs the group + * + * @return BaseBuilder + */ + public function orGroupStart() + { + return $this->groupStart('', 'OR '); + } + + //-------------------------------------------------------------------- + + /** + * Starts a query group, but NOTs the group + * + * @return BaseBuilder + */ + public function notGroupStart() + { + return $this->groupStart('NOT ', 'AND '); + } + + //-------------------------------------------------------------------- + + /** + * Starts a query group, but OR NOTs the group + * + * @return BaseBuilder + */ + public function orNotGroupStart() + { + return $this->groupStart('NOT ', 'OR '); + } + + //-------------------------------------------------------------------- + + /** + * Ends a query group + * + * @return BaseBuilder + */ + public function groupEnd() + { + $this->QBWhereGroupStarted = false; + $where = [ + 'condition' => str_repeat(' ', $this->QBWhereGroupCount--).')', + 'escape' => false, + ]; + + $this->QBWhere[] = $where; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Group_get_type + * + * @used-by groupStart() + * @used-by _like() + * @used-by whereHaving() + * @used-by _whereIn() + * + * @param string $type + * + * @return string + */ + protected function groupGetType($type) + { + if ($this->QBWhereGroupStarted) + { + $type = ''; + $this->QBWhereGroupStarted = false; + } + + return $type; + } + + //-------------------------------------------------------------------- + + /** + * GROUP BY + * + * @param string $by + * @param bool $escape + * + * @return BaseBuilder + */ + public function groupBy($by, $escape = null) + { + is_bool($escape) || $escape = $this->db->protectIdentifiers; + + if (is_string($by)) + { + $by = ($escape === true) + ? explode(',', $by) + : [$by]; + } + + foreach ($by as $val) + { + $val = trim($val); + + if ($val !== '') + { + $val = ['field' => $val, 'escape' => $escape]; + + $this->QBGroupBy[] = $val; + } + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * HAVING + * + * Separates multiple calls with 'AND'. + * + * @param string $key + * @param string $value + * @param bool $escape + * + * @return BaseBuilder + */ + public function having($key, $value = null, $escape = null) + { + return $this->whereHaving('QBHaving', $key, $value, 'AND ', $escape); + } + + //-------------------------------------------------------------------- + + /** + * OR HAVING + * + * Separates multiple calls with 'OR'. + * + * @param string $key + * @param string $value + * @param bool $escape + * + * @return BaseBuilder + */ + public function orHaving($key, $value = null, $escape = null) + { + return $this->whereHaving('QBHaving', $key, $value, 'OR ', $escape); + } + + //-------------------------------------------------------------------- + + /** + * ORDER BY + * + * @param string $orderby + * @param string $direction ASC, DESC or RANDOM + * @param bool $escape + * + * @return BaseBuilder + */ + public function orderBy($orderby, $direction = '', $escape = null) + { + $direction = strtoupper(trim($direction)); + + if ($direction === 'RANDOM') + { + $direction = ''; + + // Do we have a seed value? + $orderby = ctype_digit((string)$orderby) + ? sprintf($this->randomKeyword[1], $orderby) + : $this->randomKeyword[0]; + } + elseif (empty($orderby)) + { + return $this; + } + elseif ($direction !== '') + { + $direction = in_array($direction, ['ASC', 'DESC'], true) ? ' '.$direction : ''; + } + + is_bool($escape) || $escape = $this->db->protectIdentifiers; + + if ($escape === false) + { + $qb_orderby[] = ['field' => $orderby, 'direction' => $direction, 'escape' => false]; + } + else + { + $qb_orderby = []; + foreach (explode(',', $orderby) as $field) + { + $qb_orderby[] = ($direction === '' && + preg_match('/\s+(ASC|DESC)$/i', rtrim($field), $match, PREG_OFFSET_CAPTURE)) + ? [ + 'field' => ltrim(substr($field, 0, $match[0][1])), + 'direction' => ' '.$match[1][0], + 'escape' => true, + ] + : ['field' => trim($field), 'direction' => $direction, 'escape' => true]; + } + } + + $this->QBOrderBy = array_merge($this->QBOrderBy, $qb_orderby); + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * LIMIT + * + * @param int $value LIMIT value + * @param int $offset OFFSET value + * + * @return BaseBuilder + */ + public function limit($value, $offset = 0) + { + if (! is_null($value)) + { + $this->QBLimit = (int)$value; + } + + if (! empty($offset)) + { + $this->QBOffset = (int)$offset; + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Sets the OFFSET value + * + * @param int $offset OFFSET value + * + * @return BaseBuilder + */ + public function offset($offset) + { + if (! empty($offset)) + { + $this->QBOffset = (int)$offset; + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * LIMIT string + * + * Generates a platform-specific LIMIT clause. + * + * @param string $sql SQL Query + * + * @return string + */ + protected function _limit($sql) + { + return $sql.' LIMIT '.($this->QBOffset ? $this->QBOffset.', ' : '').$this->QBLimit; + } + + //-------------------------------------------------------------------- + + /** + * The "set" function. + * + * Allows key/value pairs to be set for inserting or updating + * + * @param mixed + * @param string + * @param bool + * + * @return BaseBuilder + */ + public function set($key, $value = '', $escape = null) + { + $key = $this->objectToArray($key); + + if ( ! is_array($key)) + { + $key = [$key => $value]; + } + + $escape = is_bool($escape) ? $escape : $this->db->protectIdentifiers; + + foreach ($key as $k => $v) + { + $this->binds[$k] = $v; + $this->QBSet[$this->db->protectIdentifiers($k, false, $escape)] = ':'.$k; + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Get SELECT query string + * + * Compiles a SELECT query string and returns the sql. + * + * @param bool TRUE: resets QB values; FALSE: leave QB values alone + * + * @return string + */ + public function getCompiledSelect($reset = true) + { + $select = $this->compileSelect(); + + if ($reset === true) + { + $this->resetSelect(); + } + + return $select; + } + + //-------------------------------------------------------------------- + + /** + * Get + * + * Compiles the select statement based on the other functions called + * and runs the query + * + * @param string the limit clause + * @param string the offset clause + * @param bool If true, returns the generate SQL, otherwise executes the query. + * + * @return CI_DB_result + */ + public function get($limit = null, $offset = null, $returnSQL = false) + { + if ( ! empty($limit)) + { + $this->limit($limit, $offset); + } + $result = $returnSQL + ? $this->getCompiledSelect() + : $this->db->query($this->compileSelect(), $this->binds); + + $this->resetSelect(); + + return $result; + } + + //-------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @param bool $test Are we running automated tests? + * + * @return int + */ + public function countAll($test = false) + { + $table = $this->QBFrom[0]; + + $sql = $this->countString.$this->db->escapeIdentifiers('numrows').' FROM '. + $this->db->protectIdentifiers($table, true, null, false); + + if ($test) + { + return $sql; + } + + $query = $this->db->query($sql); + if (count($query->getResult()) === 0) + { + return 0; + } + + $query = $query->getRow(); + $this->resetSelect(); + + return (int)$query->numrows; + } + + //-------------------------------------------------------------------- + + /** + * "Count All Results" query + * + * Generates a platform-specific query string that counts all records + * returned by an Query Builder query. + * + * @param string + * @param bool the reset clause + * + * @return int + */ + public function countAllResults($reset = true, $test = false) + { + // ORDER BY usage is often problematic here (most notably + // on Microsoft SQL Server) and ultimately unnecessary + // for selecting COUNT(*) ... + if ( ! empty($this->QBOrderBy)) + { + $orderby = $this->QBOrderBy; + $this->QBOrderBy = null; + } + + $sql = ($this->QBDistinct === true) + ? $this->countString.$this->db->protectIdentifiers('numrows')."\nFROM (\n". + $this->compileSelect()."\n) CI_count_all_results" + : $this->compileSelect($this->countString.$this->db->protectIdentifiers('numrows')); + + if ($test) + { + return $sql; + } + + $result = $this->db->query($sql, $this->binds); + + if ($reset === true) + { + $this->resetSelect(); + } + // If we've previously reset the QBOrderBy values, get them back + elseif ( ! isset($this->QBOrderBy)) + { + $this->QBOrderBy = $orderby; + } + + $row = $result->getRow(); + + if (empty($row)) + { + return 0; + } + + return (int)$row->numrows; + } + + //-------------------------------------------------------------------- + + /** + * Get_Where + * + * Allows the where clause, limit and offset to be added directly + * + * @param string $where + * @param int $limit + * @param int $offset + * + * @return CI_DB_result + */ + public function getWhere($where = null, $limit = null, $offset = null) + { + if ($where !== null) + { + $this->where($where); + } + + if ( ! empty($limit)) + { + $this->limit($limit, $offset); + } + + $result = $this->db->query($this->compileSelect(), $this->binds); + $this->resetSelect(); + + return $result; + } + + //-------------------------------------------------------------------- + + /** + * Insert_Batch + * + * Compiles batch insert strings and runs the queries + * + * @param array $set An associative array of insert values + * @param bool $escape Whether to escape values and identifiers + * + * @param int $batch_size + * @param bool $testing + * + * @return int Number of rows inserted or FALSE on failure + * @throws DatabaseException + */ + public function insertBatch($set = null, $escape = null, $batch_size = 100, $testing = false) + { + if ($set === null) + { + if (empty($this->QBSet)) + { + if (CI_DEBUG) + { + throw new DatabaseException('You must use the "set" method to update an entry.'); + } + + return false; + } + } + else + { + if (empty($set)) + { + if (CI_DEBUG) + { + throw new DatabaseException('insertBatch() called with no data'); + } + + return false; + } + + $this->setInsertBatch($set, '', $escape); + } + + $table = $this->QBFrom[0]; + + // Batch this baby + $affected_rows = 0; + for ($i = 0, $total = count($this->QBSet); $i < $total; $i += $batch_size) + { + $sql = $this->_insertBatch($this->db->protectIdentifiers($table, true, $escape, false), $this->QBKeys, + array_slice($this->QBSet, $i, $batch_size)); + + if ($testing) + { + ++$affected_rows; + } + else + { + $this->db->query($sql, $this->binds); + $affected_rows += $this->db->affectedRows(); + } + } + + if ( ! $testing) + { + $this->resetWrite(); + } + + return $affected_rows; + } + + //-------------------------------------------------------------------- + + /** + * Insert batch statement + * + * Generates a platform-specific insert string from the supplied data. + * + * @param string $table Table name + * @param array $keys INSERT keys + * @param array $values INSERT values + * + * @return string + */ + protected function _insertBatch($table, $keys, $values) + { + return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values); + } + + //-------------------------------------------------------------------- + + /** + * The "setInsertBatch" function. Allows key/value pairs to be set for batch inserts + * + * @param mixed + * @param string + * @param bool + * + * @return BaseBuilder + */ + public function setInsertBatch($key, $value = '', $escape = null) + { + $key = $this->batchObjectToArray($key); + + if ( ! is_array($key)) + { + $key = [$key => $value]; + } + + $escape = is_bool($escape) ? $escape : $this->db->protectIdentifiers; + + $keys = array_keys($this->objectToArray(current($key))); + sort($keys); + + foreach ($key as $row) + { + $row = $this->objectToArray($row); + if (count(array_diff($keys, array_keys($row))) > 0 || count(array_diff(array_keys($row), $keys)) > 0) + { + // batch function above returns an error on an empty array + $this->QBSet[] = []; + + return; + } + + ksort($row); // puts $row in the same order as our keys + + $clean = []; + foreach ($row as $k => $value) + { + $clean[] = ':'.$this->setBind($k, $value); + } + + $row = $clean; + + $this->QBSet[] = '('.implode(',', $row).')'; + } + + foreach ($keys as $k) + { + $this->QBKeys[] = $this->db->protectIdentifiers($k, false, $escape); + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Get INSERT query string + * + * Compiles an insert query and returns the sql + * + * @param bool TRUE: reset QB values; FALSE: leave QB values alone + * + * @return string + */ + public function getCompiledInsert($reset = true) + { + if ($this->validateInsert() === false) + { + return false; + } + + $sql = $this->_insert( + $this->db->protectIdentifiers( + $this->QBFrom[0], true, null, false + ), + array_keys($this->QBSet), + array_values($this->QBSet) + ); + + if ($reset === true) + { + $this->resetWrite(); + } + + return $sql; + } + + //-------------------------------------------------------------------- + + /** + * Insert + * + * Compiles an insert string and runs the query + * + * @param array an associative array of insert values + * @param bool $escape Whether to escape values and identifiers + * @param bool $test Used when running tests + * + * @return bool TRUE on success, FALSE on failure + */ + public function insert($set = null, $escape = null, $test = false) + { + if ($set !== null) + { + $this->set($set, '', $escape); + } + + if ($this->validateInsert() === false) + { + return false; + } + + $sql = $this->_insert( + $this->db->protectIdentifiers( + $this->QBFrom[0], true, $escape, false + ), + array_keys($this->QBSet), + array_values($this->QBSet) + ); + + if ($test === false) + { + $this->resetWrite(); + + return $this->db->query($sql, $this->binds); + } + } + + //-------------------------------------------------------------------- + + /** + * Validate Insert + * + * This method is used by both insert() and getCompiledInsert() to + * validate that the there data is actually being set and that table + * has been chosen to be inserted into. + * + * @param string the table to insert data into + * + * @return string + */ + protected function validateInsert() + { + if (count($this->QBSet) === 0) + { + if (CI_DEBUG) + { + throw new DatabaseException('You must use the "set" method to update an entry.'); + } + + return false; + } + + return true; + } + + //-------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @param string the table name + * @param array the insert keys + * @param array the insert values + * + * @return string + */ + protected function _insert($table, array $keys, array $unescapedKeys) + { + return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $unescapedKeys).')'; + } + + //-------------------------------------------------------------------- + + /** + * Replace + * + * Compiles an replace into string and runs the query + * + * @param array an associative array of insert values + * @param bool $returnSQL + * + * @return bool TRUE on success, FALSE on failure + * @throws DatabaseException + */ + public function replace($set = null, $returnSQL = false) + { + if ($set !== null) + { + $this->set($set); + } + + if (count($this->QBSet) === 0) + { + if (CI_DEBUG) + { + throw new DatabaseException('You must use the "set" method to update an entry.'); + } + return false; + } + + $table = $this->QBFrom[0]; + + $sql = $this->_replace($table, array_keys($this->QBSet), + array_values($this->QBSet)); + + $this->resetWrite(); + + return $returnSQL ? $sql : $this->db->query($sql, $this->binds); + } + + //-------------------------------------------------------------------- + + /** + * Replace statement + * + * Generates a platform-specific replace string from the supplied data + * + * @param string the table name + * @param array the insert keys + * @param array the insert values + * + * @return string + */ + protected function _replace($table, $keys, $values) + { + return 'REPLACE INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')'; + } + + //-------------------------------------------------------------------- + + /** + * FROM tables + * + * Groups tables in FROM clauses if needed, so there is no confusion + * about operator precedence. + * + * Note: This is only used (and overridden) by MySQL and CUBRID. + * + * @return string + */ + protected function _fromTables() + { + return implode(', ', $this->QBFrom); + } + + //-------------------------------------------------------------------- + + /** + * Get UPDATE query string + * + * Compiles an update query and returns the sql + * + * @param bool TRUE: reset QB values; FALSE: leave QB values alone + * + * @return string + */ + public function getCompiledUpdate($reset = true) + { + if ($this->validateUpdate() === false) + { + return false; + } + + $sql = $this->_update($this->QBFrom[0], $this->QBSet); + + if ($reset === true) + { + $this->resetWrite(); + } + + return $sql; + } + + //-------------------------------------------------------------------- + + /** + * UPDATE + * + * Compiles an update string and runs the query. + * + * @param array $set An associative array of update values + * @param mixed $where + * @param int $limit + * @param bool $test Are we testing the code? + * + * @return bool TRUE on success, FALSE on failure + */ + public function update($set = null, $where = null, $limit = null, $test = false) + { + if ($set !== null) + { + $this->set($set); + } + + if ($this->validateUpdate() === false) + { + return false; + } + + if ($where !== null) + { + $this->where($where); + } + + if ( ! empty($limit)) + { + $this->limit($limit); + } + + $sql = $this->_update($this->QBFrom[0], $this->QBSet); + + if ( ! $test) + { + $this->resetWrite(); + + if ($this->db->query($sql, $this->binds)) + { + return true; + } + } + } + + //-------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @param string the table name + * @param array the update data + * + * @return string + */ + protected function _update($table, $values) + { + foreach ($values as $key => $val) + { + $valstr[] = $key.' = '.$val; + } + + return 'UPDATE '.$table.' SET '.implode(', ', $valstr) + .$this->compileWhereHaving('QBWhere') + .$this->compileOrderBy() + .($this->QBLimit ? $this->_limit(' ') : ''); + } + + //-------------------------------------------------------------------- + + /** + * Validate Update + * + * This method is used by both update() and getCompiledUpdate() to + * validate that data is actually being set and that a table has been + * chosen to be update. + * + * + * @return bool + */ + protected function validateUpdate() + { + if (count($this->QBSet) === 0) + { + if (CI_DEBUG) + { + throw new DatabaseException('You must use the "set" method to update an entry.'); + } + + return false; + } + + return true; + } + + //-------------------------------------------------------------------- + + /** + * Update_Batch + * + * Compiles an update string and runs the query + * + * @param array an associative array of update values + * @param string the where key + * @param int The size of the batch to run + * @param bool true means SQL is returned, false will execute the query + * + * @return int number of rows affected or FALSE on failure + */ + public function updateBatch($set = null, $index = null, $batch_size = 100, $returnSQL = false) + { + if ($index === null) + { + if (CI_DEBUG) + { + throw new DatabaseException('You must specify an index to match on for batch updates.'); + } + + return false; + } + + if ($set === null) + { + if (empty($this->QBSet)) + { + if (CI_DEBUG) + { + throw new DatabaseException('You must use the "set" method to update an entry.'); + } + return false; + } + } + else + { + if (empty($set)) + { + if (CI_DEBUG) + { + throw new DatabaseException('updateBatch() called with no data'); + } + return false; + } + + $this->setUpdateBatch($set, $index); + } + + $table = $this->QBFrom[0]; + + // Batch this baby + $affected_rows = 0; + $savedSQL = []; + for ($i = 0, $total = count($this->QBSet); $i < $total; $i += $batch_size) + { + $sql = $this->_updateBatch($table, + array_slice($this->QBSet, $i, $batch_size), + $this->db->protectIdentifiers($index) + ); + + if ($returnSQL) + { + $savedSQL[] = $sql; + } + else + { + $this->db->query($sql, $this->binds); + $affected_rows += $this->db->affectedRows(); + } + + $this->QBWhere = []; + } + + $this->resetWrite(); + + return $returnSQL ? $savedSQL : $affected_rows; + } + + //-------------------------------------------------------------------- + + /** + * Update_Batch statement + * + * Generates a platform-specific batch update string from the supplied data + * + * @param string $table Table name + * @param array $values Update data + * @param string $index WHERE key + * + * @return string + */ + protected function _updateBatch($table, $values, $index) + { + $ids = []; + foreach ($values as $key => $val) + { + $ids[] = $val[$index]; + + foreach (array_keys($val) as $field) + { + if ($field !== $index) + { + $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field]; + } + } + } + + $cases = ''; + foreach ($final as $k => $v) + { + $cases .= $k." = CASE \n" + .implode("\n", $v)."\n" + .'ELSE '.$k.' END, '; + } + + $this->where($index.' IN('.implode(',', $ids).')', null, false); + + return 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->compileWhereHaving('QBWhere'); + } + + //-------------------------------------------------------------------- + + /** + * The "setUpdateBatch" function. Allows key/value pairs to be set for batch updating + * + * @param array + * @param string + * @param bool + * + * @return BaseBuilder + */ + public function setUpdateBatch($key, $index = '', $escape = null) + { + $key = $this->batchObjectToArray($key); + + if ( ! is_array($key)) + { + // @todo error + } + + is_bool($escape) || $escape = $this->db->protectIdentifiers; + + foreach ($key as $k => $v) + { + $index_set = false; + $clean = []; + foreach ($v as $k2 => $v2) + { + if ($k2 === $index) + { + $index_set = true; + } + + $bind = $this->setBind($k2, $v2); + + $clean[$this->db->protectIdentifiers($k2, false, $escape)] = ':'.$bind; + } + + if ($index_set === false) + { + throw new DatabaseException('One or more rows submitted for batch updating is missing the specified index.'); + } + + $this->QBSet[] = $clean; + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Empty Table + * + * Compiles a delete string and runs "DELETE FROM table" + * + * @param bool $test + * @return bool TRUE on success, FALSE on failure + */ + public function emptyTable($test = false) + { + $table = $this->QBFrom[0]; + + $sql = $this->_delete($table); + + if ($test) + { + return $sql; + } + + $this->resetWrite(); + + return $this->db->query($sql); + } + + //-------------------------------------------------------------------- + + /** + * Truncate + * + * Compiles a truncate string and runs the query + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @param bool Whether we're in test mode or not. + * + * @return bool TRUE on success, FALSE on failure + */ + public function truncate($test = false) + { + $table = $this->QBFrom[0]; + + $sql = $this->_truncate($table); + + if ($test === true) + { + return $sql; + } + + $this->resetWrite(); + + return $this->db->query($sql); + } + + //-------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * + * If the database does not support the truncate() command, + * then this method maps to 'DELETE FROM table' + * + * @param string the table name + * + * @return string + */ + protected function _truncate($table) + { + return 'TRUNCATE '.$table; + } + + //-------------------------------------------------------------------- + + /** + * Get DELETE query string + * + * Compiles a delete query string and returns the sql + * + * @param string the table to delete from + * @param bool TRUE: reset QB values; FALSE: leave QB values alone + * + * @return string + */ + public function getCompiledDelete($reset = true) + { + $table = $this->QBFrom[0]; + + $this->returnDeleteSQL = true; + $sql = $this->delete($table, '', null, $reset); + $this->returnDeleteSQL = false; + + return $sql; + } + + //-------------------------------------------------------------------- + + /** + * Delete + * + * Compiles a delete string and runs the query + * + * @param mixed $where the where clause + * @param mixed $limit the limit clause + * @param bool $reset_data + * @param bool $returnSQL + * + * @return mixed + */ + public function delete($where = '', $limit = null, $reset_data = true, $returnSQL = false) + { + $table = $this->db->protectIdentifiers($this->QBFrom[0], true, null, false); + + if ($where !== '') + { + $this->where($where); + } + + if (count($this->QBWhere) === 0) + { + if (CI_DEBUG) + { + throw new DatabaseException('Deletes are not allowed unless they contain a "where" or "like" clause.'); + } + + return false; + } + + $sql = $this->_delete($table); + + if ( ! empty($limit)) + { + $this->QBLimit = $limit; + } + + if (! empty($this->QBLimit)) + { + $sql = $this->_limit($sql); + } + + if ($reset_data) + { + $this->resetWrite(); + } + + return ($returnSQL === true) ? $sql : $this->db->query($sql, $this->binds); + } + + //-------------------------------------------------------------------- + + /** + * Increments a numeric column by the specified value. + * + * @param string $column + * @param int $value + * + * @return bool + */ + public function increment(string $column, int $value = 1) + { + $column = $this->db->protectIdentifiers($column); + + $sql = $this->_update($this->QBFrom[0], [$column => "{$column} + {$value}"]); + + return $this->db->query($sql, $this->binds); + } + + //-------------------------------------------------------------------- + + /** + * Decrements a numeric column by the specified value. + * + * @param string $column + * @param int $value + * + * @return bool + */ + public function decrement(string $column, int $value = 1) + { + $column = $this->db->protectIdentifiers($column); + + $sql = $this->_update($this->QBFrom[0], [$column => "{$column}-{$value}"]); + + return $this->db->query($sql, $this->binds); + } + + //-------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @param string the table name + * + * @return string + */ + protected function _delete($table) + { + return 'DELETE FROM '.$table.$this->compileWhereHaving('QBWhere') + .($this->QBLimit ? ' LIMIT '.$this->QBLimit : ''); + } + + //-------------------------------------------------------------------- + + /** + * Track Aliases + * + * Used to track SQL statements written with aliased tables. + * + * @param string The table to inspect + * + * @return string + */ + protected function trackAliases($table) + { + if (is_array($table)) + { + foreach ($table as $t) + { + $this->trackAliases($t); + } + + return; + } + + // Does the string contain a comma? If so, we need to separate + // the string into discreet statements + if (strpos($table, ',') !== false) + { + return $this->trackAliases(explode(',', $table)); + } + + // if a table alias is used we can recognize it by a space + if (strpos($table, ' ') !== false) + { + // if the alias is written with the AS keyword, remove it + $table = preg_replace('/\s+AS\s+/i', ' ', $table); + + // Grab the alias + $table = trim(strrchr($table, ' ')); + + // Store the alias, if it doesn't already exist + if ( ! in_array($table, $this->QBAliasedTables)) + { + $this->QBAliasedTables[] = $table; + } + } + } + + //-------------------------------------------------------------------- + + /** + * Compile the SELECT statement + * + * Generates a query string based on which functions were used. + * Should not be called directly. + * + * @param bool $select_override + * + * @return string + */ + protected function compileSelect($select_override = false) + { + // Write the "select" portion of the query + if ($select_override !== false) + { + $sql = $select_override; + } + else + { + $sql = ( ! $this->QBDistinct) ? 'SELECT ' : 'SELECT DISTINCT '; + + if (count($this->QBSelect) === 0) + { + $sql .= '*'; + } + else + { + // Cycle through the "select" portion of the query and prep each column name. + // The reason we protect identifiers here rather than in the select() function + // is because until the user calls the from() function we don't know if there are aliases + foreach ($this->QBSelect as $key => $val) + { + $no_escape = isset($this->QBNoEscape[$key]) ? $this->QBNoEscape[$key] : null; + $this->QBSelect[$key] = $this->db->protectIdentifiers($val, false, $no_escape); + } + + $sql .= implode(', ', $this->QBSelect); + } + } + + // Write the "FROM" portion of the query + if (count($this->QBFrom) > 0) + { + $sql .= "\nFROM ".$this->_fromTables(); + } + + // Write the "JOIN" portion of the query + if (count($this->QBJoin) > 0) + { + $sql .= "\n".implode("\n", $this->QBJoin); + } + + $sql .= $this->compileWhereHaving('QBWhere') + .$this->compileGroupBy() + .$this->compileWhereHaving('QBHaving') + .$this->compileOrderBy(); // ORDER BY + + // LIMIT + if ($this->QBLimit) + { + return $this->_limit($sql."\n"); + } + + return $sql; + } + + //-------------------------------------------------------------------- + + /** + * Compile WHERE, HAVING statements + * + * Escapes identifiers in WHERE and HAVING statements at execution time. + * + * Required so that aliases are tracked properly, regardless of whether + * where(), orWhere(), having(), orHaving are called prior to from(), + * join() and prefixTable is added only if needed. + * + * @param string $qb_key 'QBWhere' or 'QBHaving' + * + * @return string SQL statement + */ + protected function compileWhereHaving($qb_key) + { + if (count($this->$qb_key) > 0) + { + for ($i = 0, $c = count($this->$qb_key); $i < $c; $i++) + { + // Is this condition already compiled? + if (is_string($this->{$qb_key}[$i])) + { + continue; + } + elseif ($this->{$qb_key}[$i]['escape'] === false) + { + $this->{$qb_key}[$i] = $this->{$qb_key}[$i]['condition']; + continue; + } + + // Split multiple conditions + $conditions = preg_split( + '/((?:^|\s+)AND\s+|(?:^|\s+)OR\s+)/i', + $this->{$qb_key}[$i]['condition'], + -1, + PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY + ); + + for ($ci = 0, $cc = count($conditions); $ci < $cc; $ci++) + { + if (($op = $this->getOperator($conditions[$ci])) === false + OR + ! preg_match('/^(\(?)(.*)('.preg_quote($op, '/').')\s*(.*(? '(test <= foo)', /* the whole thing */ + // 1 => '(', /* optional */ + // 2 => 'test', /* the field name */ + // 3 => ' <= ', /* $op */ + // 4 => 'foo', /* optional, if $op is e.g. 'IS NULL' */ + // 5 => ')' /* optional */ + // ); + + if ( ! empty($matches[4])) + { +// $this->isLiteral($matches[4]) OR $matches[4] = $this->db->protectIdentifiers(trim($matches[4])); + $matches[4] = ' '.$matches[4]; + } + + $conditions[$ci] = $matches[1].$this->db->protectIdentifiers(trim($matches[2])) + .' '.trim($matches[3]).$matches[4].$matches[5]; + } + + $this->{$qb_key}[$i] = implode('', $conditions); + } + + return ($qb_key === 'QBHaving' ? "\nHAVING " : "\nWHERE ") + .implode("\n", $this->$qb_key); + } + + return ''; + } + + //-------------------------------------------------------------------- + + /** + * Compile GROUP BY + * + * Escapes identifiers in GROUP BY statements at execution time. + * + * Required so that aliases are tracked properly, regardless of wether + * groupBy() is called prior to from(), join() and prefixTable is added + * only if needed. + * + * @return string SQL statement + */ + protected function compileGroupBy() + { + if (count($this->QBGroupBy) > 0) + { + for ($i = 0, $c = count($this->QBGroupBy); $i < $c; $i++) + { + // Is it already compiled? + if (is_string($this->QBGroupBy[$i])) + { + continue; + } + + $this->QBGroupBy[$i] = ($this->QBGroupBy[$i]['escape'] === false OR + $this->isLiteral($this->QBGroupBy[$i]['field'])) + ? $this->QBGroupBy[$i]['field'] + : $this->db->protectIdentifiers($this->QBGroupBy[$i]['field']); + } + + return "\nGROUP BY ".implode(', ', $this->QBGroupBy); + } + + return ''; + } + + //-------------------------------------------------------------------- + + /** + * Compile ORDER BY + * + * Escapes identifiers in ORDER BY statements at execution time. + * + * Required so that aliases are tracked properly, regardless of wether + * orderBy() is called prior to from(), join() and prefixTable is added + * only if needed. + * + * @return string SQL statement + */ + protected function compileOrderBy() + { + if (is_array($this->QBOrderBy) && count($this->QBOrderBy) > 0) + { + for ($i = 0, $c = count($this->QBOrderBy); $i < $c; $i++) + { + if ($this->QBOrderBy[$i]['escape'] !== false && ! $this->isLiteral($this->QBOrderBy[$i]['field'])) + { + $this->QBOrderBy[$i]['field'] = $this->db->protectIdentifiers($this->QBOrderBy[$i]['field']); + } + + $this->QBOrderBy[$i] = $this->QBOrderBy[$i]['field'].$this->QBOrderBy[$i]['direction']; + } + + return $this->QBOrderBy = "\nORDER BY ".implode(', ', $this->QBOrderBy); + } + elseif (is_string($this->QBOrderBy)) + { + return $this->QBOrderBy; + } + + return ''; + } + + //-------------------------------------------------------------------- + + /** + * Object to Array + * + * Takes an object as input and converts the class variables to array key/vals + * + * @param object + * + * @return array + */ + protected function objectToArray($object) + { + if ( ! is_object($object)) + { + return $object; + } + + $array = []; + foreach (get_object_vars($object) as $key => $val) + { + // There are some built in keys we need to ignore for this conversion + if ( ! is_object($val) && ! is_array($val) && $key !== '_parent_name') + { + $array[$key] = $val; + } + } + + return $array; + } + + //-------------------------------------------------------------------- + + /** + * Object to Array + * + * Takes an object as input and converts the class variables to array key/vals + * + * @param object + * + * @return array + */ + protected function batchObjectToArray($object) + { + if ( ! is_object($object)) + { + return $object; + } + + $array = []; + $out = get_object_vars($object); + $fields = array_keys($out); + + foreach ($fields as $val) + { + // There are some built in keys we need to ignore for this conversion + if ($val !== '_parent_name') + { + $i = 0; + foreach ($out[$val] as $data) + { + $array[$i++][$val] = $data; + } + } + } + + return $array; + } + + //-------------------------------------------------------------------- + + /** + * Is literal + * + * Determines if a string represents a literal value or a field name + * + * @param string $str + * + * @return bool + */ + protected function isLiteral($str) + { + $str = trim($str); + + if (empty($str) || ctype_digit($str) || (string)(float)$str === $str || + in_array(strtoupper($str), ['TRUE', 'FALSE'], true) + ) + { + return true; + } + + static $_str; + + if (empty($_str)) + { + $_str = ($this->db->escapeChar !== '"') + ? ['"', "'"] : ["'"]; + } + + return in_array($str[0], $_str, true); + } + + //-------------------------------------------------------------------- + + /** + * Reset Query Builder values. + * + * Publicly-visible method to reset the QB values. + * + * @return BaseBuilder + */ + public function resetQuery() + { + $this->resetSelect(); + $this->resetWrite(); + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Resets the query builder values. Called by the get() function + * + * @param array An array of fields to reset + * + * @return void + */ + protected function resetRun($qb_reset_items) + { + foreach ($qb_reset_items as $item => $default_value) + { + $this->$item = $default_value; + } + } + + //-------------------------------------------------------------------- + + /** + * Resets the query builder values. Called by the get() function + * + * @return void + */ + protected function resetSelect() + { + $this->resetRun([ + 'QBSelect' => [], + 'QBJoin' => [], + 'QBWhere' => [], + 'QBGroupBy' => [], + 'QBHaving' => [], + 'QBOrderBy' => [], + 'QBAliasedTables' => [], + 'QBNoEscape' => [], + 'QBDistinct' => false, + 'QBLimit' => false, + 'QBOffset' => false, + ]); + } + + //-------------------------------------------------------------------- + + /** + * Resets the query builder "write" values. + * + * Called by the insert() update() insertBatch() updateBatch() and delete() functions + * + * @return void + */ + protected function resetWrite() + { + $this->resetRun([ + 'QBSet' => [], + 'QBJoin' => [], + 'QBWhere' => [], + 'QBOrderBy' => [], + 'QBKeys' => [], + 'QBLimit' => false, + ]); + } + + //-------------------------------------------------------------------- + + /** + * Tests whether the string has an SQL operator + * + * @param string + * + * @return bool + */ + protected function hasOperator($str) + { + return (bool)preg_match('/(<|>|!|=|\sIS NULL|\sIS NOT NULL|\sEXISTS|\sBETWEEN|\sLIKE|\sIN\s*\(|\s)/i', + trim($str)); + } + + // -------------------------------------------------------------------- + + /** + * Returns the SQL string operator + * + * @param string + * + * @return string + */ + protected function getOperator($str) + { + static $_operators; + + if (empty($_operators)) + { + $_les = ($this->db->likeEscapeStr !== '') + ? '\s+'.preg_quote(trim(sprintf($this->db->likeEscapeStr, $this->db->likeEscapeChar)), '/') + : ''; + $_operators = [ + '\s*(?:<|>|!)?=\s*', // =, <=, >=, != + '\s*<>?\s*', // <, <> + '\s*>\s*', // > + '\s+IS NULL', // IS NULL + '\s+IS NOT NULL', // IS NOT NULL + '\s+EXISTS\s*\(.*\)', // EXISTS(sql) + '\s+NOT EXISTS\s*\(.*\)', // NOT EXISTS(sql) + '\s+BETWEEN\s+', // BETWEEN value AND value + '\s+IN\s*\(.*\)', // IN(list) + '\s+NOT IN\s*\(.*\)', // NOT IN (list) + '\s+LIKE\s+\S.*('.$_les.')?', // LIKE 'expr'[ ESCAPE '%s'] + '\s+NOT LIKE\s+\S.*('.$_les.')?' // NOT LIKE 'expr'[ ESCAPE '%s'] + ]; + } + + return preg_match('/'.implode('|', $_operators).'/i', $str, $match) + ? $match[0] : false; + } + + // -------------------------------------------------------------------- + + /** + * Stores a bind value after ensuring that it's unique. + * + * @param string $key + * @param null $value + * + * @return string + */ + protected function setBind(string $key, $value = null) + { + if ( ! array_key_exists($key, $this->binds)) + { + $this->binds[$key] = $value; + + return $key; + } + + $count = 0; + + while (array_key_exists($key.$count, $this->binds)) + { + ++$count; + } + + $this->binds[$key.$count] = $value; + + return $key.$count; + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Database/BaseConnection.php b/ci-4.0-dev/system/Database/BaseConnection.php new file mode 100644 index 000000000..2218e1e77 --- /dev/null +++ b/ci-4.0-dev/system/Database/BaseConnection.php @@ -0,0 +1,1670 @@ + $value) + { + $this->$key = $value; + } + } + + //-------------------------------------------------------------------- + + + /** + * Initializes the database connection/settings. + * + * @return mixed + */ + public function initialize() + { + /* If an established connection is available, then there's + * no need to connect and select the database. + * + * Depending on the database driver, conn_id can be either + * boolean TRUE, a resource or an object. + */ + if ($this->connID) + { + return; + } + + //-------------------------------------------------------------------- + + $this->connectTime = microtime(true); + + // Connect to the database and set the connection ID + $this->connID = $this->connect($this->pConnect); + + // No connection resource? Check if there is a failover else throw an error + if ( ! $this->connID) + { + // Check if there is a failover set + if ( ! empty($this->failover) && is_array($this->failover)) + { + // Go over all the failovers + foreach ($this->failover as $failover) + { + // Replace the current settings with those of the failover + foreach ($failover as $key => $val) + { + $this->$key = $val; + } + + // Try to connect + $this->connID = $this->connect($this->pConnect); + + // If a connection is made break the foreach loop + if ($this->connID) + { + break; + } + } + } + + // We still don't have a connection? + if ( ! $this->connID) + { + throw new DatabaseException('Unable to connect to the database.'); + } + } + + $this->connectDuration = microtime(true) - $this->connectTime; + } + + //-------------------------------------------------------------------- + + /** + * Connect to the database. + * + * @param bool $persistent + * @return mixed + */ + abstract public function connect($persistent = false); + + //-------------------------------------------------------------------- + + /** + * Close the database connection. + */ + public function close() + { + if ($this->connID) + { + $this->_close(); + $this->connID = FALSE; + } + } + + //-------------------------------------------------------------------- + + /** + * Platform dependent way method for closing the connection. + * + * @return mixed + */ + abstract protected function _close(); + + //-------------------------------------------------------------------- + + /** + * Create a persistent database connection. + * + * @return mixed + */ + public function persistentConnect() + { + return $this->connect(true); + } + + //-------------------------------------------------------------------- + + /** + * Keep or establish the connection if no queries have been sent for + * a length of time exceeding the server's idle timeout. + * + * @return mixed + */ + abstract public function reconnect(); + + //-------------------------------------------------------------------- + + /** + * Returns the actual connection object. If both a 'read' and 'write' + * connection has been specified, you can pass either term in to + * get that connection. If you pass either alias in and only a single + * connection is present, it must return the sole connection. + * + * @param string|null $alias + * + * @return mixed + */ + public function getConnection(string $alias = null) + { + //@todo work with read/write connections + return $this->connID; + } + + //-------------------------------------------------------------------- + + /** + * Select a specific database table to use. + * + * @param string $databaseName + * + * @return mixed + */ + abstract public function setDatabase(string $databaseName); + + //-------------------------------------------------------------------- + + /** + * Returns the name of the current database being used. + * + * @return string + */ + public function getDatabase(): string + { + return empty($this->database) ? '' : $this->database; + } + + //-------------------------------------------------------------------- + + /** + * Returns the last error encountered by this connection. + * + * @return mixed + */ + public function getError() + { + } + + //-------------------------------------------------------------------- + + /** + * The name of the platform in use (MySQLi, mssql, etc) + * + * @return mixed + */ + public function getPlatform() + { + return $this->DBDriver; + } + + //-------------------------------------------------------------------- + + /** + * Returns a string containing the version of the database being used. + * + * @return mixed + */ + abstract public function getVersion(); + + //-------------------------------------------------------------------- + + /** + * Executes the query against the database. + * + * @param $sql + * + * @return mixed + */ + abstract protected function execute($sql); + + //-------------------------------------------------------------------- + + + /** + * Orchestrates a query against the database. Queries must use + * Database\Statement objects to store the query and build it. + * This method works with the cache. + * + * Should automatically handle different connections for read/write + * queries if needed. + * + * @param string $sql + * @param array ...$binds + * @param $queryClass + * @return mixed + */ + public function query(string $sql, $binds = null, $queryClass = 'CodeIgniter\\Database\\Query') + { + if (empty($this->connID)) + { + $this->initialize(); + } + + $resultClass = str_replace('Connection', 'Result', get_class($this)); + + $query = new $queryClass($this); + + $query->setQuery($sql, $binds); + + if (! empty($this->swapPre) && ! empty($this->DBPrefix)) + { + $query->swapPrefix($this->DBPrefix, $this->swapPre); + } + + $startTime = microtime(true); + + // Always save the last query so we can use + // the getLastQuery() method. + $this->lastQuery = $query; + + // Run the query for real + if (! $this->pretend && false === ($this->resultID = $this->simpleQuery($query->getQuery()))) + { + $query->setDuration($startTime, $startTime); + + // This will trigger a rollback if transactions are being used + if ($this->_trans_depth !== 0) + { + $this->_trans_status = false; + } + + // @todo deal with errors + + if ($this->DBDebug) + { + // We call this function in order to roll-back queries + // if transactions are enabled. If we don't call this here + // the error message will trigger an exit, causing the + // transactions to remain in limbo. + while ($this->_trans_depth !== 0) + { + $transDepth = $this->_trans_depth; + $this->transComplete(); + + if ($transDepth === $this->_trans_depth) + { + // @todo log + // log_message('error', 'Database: Failure during an automated transaction commit/rollback!'); + break; + } + } + + // display the errors.... + // @todo display the error... + + return false; + } + + if (! $this->pretend) + { + // Let others do something with this query. + Hooks::trigger('DBQuery', $query); + } + + return new $resultClass($this->connID, $this->resultID); + } + + $query->setDuration($startTime); + + if (! $this->pretend) + { + // Let others do somethign with this query + Hooks::trigger('DBQuery', $query); + } + + // If $pretend is true, then we just want to return + // the actual query object here. There won't be + // any results to return. + return $this->pretend + ? $query + : new $resultClass($this->connID, $this->resultID); + } + + //-------------------------------------------------------------------- + + /** + * Performs a basic query against the database. No binding or caching + * is performed, nor are transactions handled. Simply takes a raw + * query string and returns the database-specific result id. + * + * @param string $sql + * + * @return mixed + */ + public function simpleQuery(string $sql) + { + if (empty($this->connID)) + { + $this->initialize(); + } + + return $this->execute($sql); + } + + //-------------------------------------------------------------------- + + /** + * Disable Transactions + * + * This permits transactions to be disabled at run-time. + */ + public function transOff() + { + $this->trans_enabled = FALSE; + } + + //-------------------------------------------------------------------- + + /** + * Enable/disable Transaction Strict Mode + * + * When strict mode is enabled, if you are running multiple groups of + * transactions, if one group fails all subsequent groups will be + * rolled back. + * + * If strict mode is disabled, each group is treated autonomously, + * meaning a failure of one group will not affect any others + * + * @param bool $mode = true + * + * @return $this + */ + public function transStrict(bool $mode=true) + { + $this->trans_strict = $mode; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Start Transaction + * + * @param bool $test_mode = FALSE + * @return bool + */ + public function transStart($test_mode = false) + { + if ( ! $this->trans_enabled) + { + return false; + } + + return $this->transBegin($test_mode); + } + + //-------------------------------------------------------------------- + + /** + * Complete Transaction + * + * @return bool + */ + public function transComplete() + { + if ( ! $this->trans_enabled) + { + return false; + } + + // The query() function will set this flag to FALSE in the event that a query failed + if ($this->_trans_status === false OR $this->_trans_failure === true) + { + $this->transRollback(); + + // If we are NOT running in strict mode, we will reset + // the _trans_status flag so that subsequent groups of + // transactions will be permitted. + if ($this->trans_strict === false) + { + $this->_trans_status = true; + } + +// log_message('debug', 'DB Transaction Failure'); + return FALSE; + } + + return $this->transCommit(); + } + + //-------------------------------------------------------------------- + + /** + * Lets you retrieve the transaction flag to determine if it has failed + * + * @return bool + */ + public function transStatus(): bool + { + return $this->_trans_status; + } + + //-------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @param bool $test_mode + * @return bool + */ + public function transBegin(bool $test_mode = false): bool + { + if ( ! $this->trans_enabled) + { + return false; + } + // When transactions are nested we only begin/commit/rollback the outermost ones + elseif ($this->_trans_depth > 0) + { + $this->_trans_depth++; + return true; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === true); + + if ($this->_transBegin()) + { + $this->_trans_depth++; + return true; + } + + return false; + } + + //-------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + public function transCommit(): bool + { + if ( ! $this->trans_enabled || $this->_trans_depth === 0) + { + return false; + } + // When transactions are nested we only begin/commit/rollback the outermost ones + elseif ($this->_trans_depth > 1 || $this->_transCommit()) + { + $this->_trans_depth--; + return true; + } + + return false; + } + + //-------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + public function transRollback(): bool + { + if ( ! $this->trans_enabled OR $this->_trans_depth === 0) + { + return false; + } + // When transactions are nested we only begin/commit/rollback the outermost ones + elseif ($this->_trans_depth > 1 OR $this->_transRollback()) + { + $this->_trans_depth--; + return true; + } + + return false; + } + + //-------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + abstract protected function _transBegin(): bool; + + //-------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + abstract protected function _transCommit(): bool; + + //-------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + abstract protected function _transRollback(): bool; + + //-------------------------------------------------------------------- + + /** + * Returns an instance of the query builder for this connection. + * + * @param string $tableName + * + * @return BaseBuilder + * @throws DatabaseException + */ + public function table($tableName) + { + if (empty($tableName)) + { + throw new DatabaseException('You must set the database table to be used with your query.'); + } + + $className = str_replace('Connection', 'Builder', get_class($this)); + + return new $className($tableName, $this); + } + + //-------------------------------------------------------------------- + + /** + * Creates a prepared statement with the database that can then + * be used to execute multiple statements against. Within the + * closure, you would build the query in any normal way, though + * the Query Builder is the expected manner. + * + * Example: + * $stmt = $db->prepare(function($db) + * { + * return $db->table('users') + * ->where('id', 1) + * ->get(); + * }) + * + * @param \Closure $func + * @param array $options Passed to the prepare() method + * + * @return PreparedQueryInterface|null + */ + public function prepare(\Closure $func, array $options = []) + { + $this->pretend(true); + + $sql = $func($this); + + $this->pretend(false); + + if ($sql instanceof QueryInterface) + { + $sql = $sql->getOriginalQuery(); + } + + $class = str_ireplace('Connection', 'PreparedQuery', get_class($this)); + $class = new $class($this); + + return $class->prepare($sql, $options); + } + + //-------------------------------------------------------------------- + + /** + * Returns the last query's statement object. + * + * @return mixed + */ + public function getLastQuery() + { + return $this->lastQuery; + } + + //-------------------------------------------------------------------- + + /** + * Returns a string representation of the last query's statement object. + * + * @return string + */ + public function showLastQuery() + { + return (string)$this->lastQuery; + } + + //-------------------------------------------------------------------- + + + + /** + * Returns the time we started to connect to this database in + * seconds with microseconds. + * + * Used by the Debug Toolbar's timeline. + * + * @return float + */ + public function getConnectStart() + { + return $this->connectTime; + } + + //-------------------------------------------------------------------- + + /** + * Returns the number of seconds with microseconds that it took + * to connect to the database. + * + * Used by the Debug Toolbar's timeline. + * + * @param int $decimals + * + * @return mixed + */ + public function getConnectDuration($decimals = 6) + { + return number_format($this->connectDuration, $decimals); + } + + //-------------------------------------------------------------------- + + /** + * Protect Identifiers + * + * This function is used extensively by the Query Builder class, and by + * a couple functions in this class. + * It takes a column or table name (optionally with an alias) and inserts + * the table prefix onto it. Some logic is necessary in order to deal with + * column names that include the path. Consider a query like this: + * + * SELECT hostname.database.table.column AS c FROM hostname.database.table + * + * Or a query with aliasing: + * + * SELECT m.member_id, m.member_name FROM members AS m + * + * Since the column name can include up to four segments (host, DB, table, column) + * or also have an alias prefix, we need to do a bit of work to figure this out and + * insert the table prefix (if it exists) in the proper position, and escape only + * the correct identifiers. + * + * @param string|array + * @param bool + * @param mixed + * @param bool + * + * @return string + */ + public function protectIdentifiers($item, $prefixSingle = false, $protectIdentifiers = null, $fieldExists = true) + { + if ( ! is_bool($protectIdentifiers)) + { + $protectIdentifiers = $this->protectIdentifiers; + } + + if (is_array($item)) + { + $escaped_array = []; + foreach ($item as $k => $v) + { + $escaped_array[$this->protectIdentifiers($k)] = $this->protectIdentifiers($v, $prefixSingle, + $protectIdentifiers, $fieldExists); + } + + return $escaped_array; + } + + // This is basically a bug fix for queries that use MAX, MIN, etc. + // If a parenthesis is found we know that we do not need to + // escape the data or add a prefix. There's probably a more graceful + // way to deal with this, but I'm not thinking of it -- Rick + // + // Added exception for single quotes as well, we don't want to alter + // literal strings. -- Narf + if (strcspn($item, "()'") !== strlen($item)) + { + return $item; + } + + // Convert tabs or multiple spaces into single spaces + $item = preg_replace('/\s+/', ' ', trim($item)); + + // If the item has an alias declaration we remove it and set it aside. + // Note: strripos() is used in order to support spaces in table names + if ($offset = strripos($item, ' AS ')) + { + $alias = ($protectIdentifiers) + ? substr($item, $offset, 4).$this->escapeIdentifiers(substr($item, $offset + 4)) + : substr($item, $offset); + $item = substr($item, 0, $offset); + } + elseif ($offset = strrpos($item, ' ')) + { + $alias = ($protectIdentifiers) + ? ' '.$this->escapeIdentifiers(substr($item, $offset + 1)) + : substr($item, $offset); + $item = substr($item, 0, $offset); + } + else + { + $alias = ''; + } + + // Break the string apart if it contains periods, then insert the table prefix + // in the correct location, assuming the period doesn't indicate that we're dealing + // with an alias. While we're at it, we will escape the components + if (strpos($item, '.') !== false) + { + $parts = explode('.', $item); + + // Does the first segment of the exploded item match + // one of the aliases previously identified? If so, + // we have nothing more to do other than escape the item + // + // NOTE: The ! empty() condition prevents this method + // from breaking when QB isn't enabled. + if ( ! empty($this->qb_aliased_tables) && in_array($parts[0], $this->qb_aliased_tables)) + { + if ($protectIdentifiers === true) + { + foreach ($parts as $key => $val) + { + if ( ! in_array($val, $this->reservedIdentifiers)) + { + $parts[$key] = $this->escapeIdentifiers($val); + } + } + + $item = implode('.', $parts); + } + + return $item.$alias; + } + + // Is there a table prefix defined in the config file? If not, no need to do anything + if ($this->DBPrefix !== '') + { + // We now add the table prefix based on some logic. + // Do we have 4 segments (hostname.database.table.column)? + // If so, we add the table prefix to the column name in the 3rd segment. + if (isset($parts[3])) + { + $i = 2; + } + // Do we have 3 segments (database.table.column)? + // If so, we add the table prefix to the column name in 2nd position + elseif (isset($parts[2])) + { + $i = 1; + } + // Do we have 2 segments (table.column)? + // If so, we add the table prefix to the column name in 1st segment + else + { + $i = 0; + } + + // This flag is set when the supplied $item does not contain a field name. + // This can happen when this function is being called from a JOIN. + if ($fieldExists === false) + { + $i++; + } + + // Verify table prefix and replace if necessary + if ($this->swapPre !== '' && strpos($parts[$i], $this->swapPre) === 0) + { + $parts[$i] = preg_replace('/^'.$this->swapPre.'(\S+?)/', $this->DBPrefix.'\\1', $parts[$i]); + } + // We only add the table prefix if it does not already exist + elseif (strpos($parts[$i], $this->DBPrefix) !== 0) + { + $parts[$i] = $this->DBPrefix.$parts[$i]; + } + + // Put the parts back together + $item = implode('.', $parts); + } + + if ($protectIdentifiers === true) + { + $item = $this->escapeIdentifiers($item); + } + + return $item.$alias; + } + + // In some cases, especially 'from', we end up running through + // protect_identifiers twice. This algorithm won't work when + // it contains the escapeChar so strip it out. + $item = trim($item, $this->escapeChar); + + // Is there a table prefix? If not, no need to insert it + if ($this->DBPrefix !== '') + { + // Verify table prefix and replace if necessary + if ($this->swapPre !== '' && strpos($item, $this->swapPre) === 0) + { + $item = preg_replace('/^'.$this->swapPre.'(\S+?)/', $this->DBPrefix.'\\1', $item); + } + // Do we prefix an item with no segments? + elseif ($prefixSingle === true && strpos($item, $this->DBPrefix) !== 0) + { + $item = $this->DBPrefix.$item; + } + } + + if ($protectIdentifiers === true && ! in_array($item, $this->reservedIdentifiers)) + { + $item = $this->escapeIdentifiers($item); + } + + return $item.$alias; + } + + //-------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @param mixed + * + * @return mixed + */ + public function escapeIdentifiers($item) + { + if ($this->escapeChar === '' OR empty($item) OR in_array($item, $this->reservedIdentifiers)) + { + return $item; + } + elseif (is_array($item)) + { + foreach ($item as $key => $value) + { + $item[$key] = $this->escapeIdentifiers($value); + } + + return $item; + } + // Avoid breaking functions and literal values inside queries + elseif (ctype_digit($item) OR $item[0] === "'" OR ($this->escapeChar !== '"' && $item[0] === '"') OR + strpos($item, '(') !== false + ) + { + return $item; + } + + static $preg_ec = []; + + if (empty($preg_ec)) + { + if (is_array($this->escapeChar)) + { + $preg_ec = [ + preg_quote($this->escapeChar[0], '/'), + preg_quote($this->escapeChar[1], '/'), + $this->escapeChar[0], + $this->escapeChar[1], + ]; + } + else + { + $preg_ec[0] = $preg_ec[1] = preg_quote($this->escapeChar, '/'); + $preg_ec[2] = $preg_ec[3] = $this->escapeChar; + } + } + + foreach ($this->reservedIdentifiers as $id) + { + if (strpos($item, '.'.$id) !== false) + { + return preg_replace('/'.$preg_ec[0].'?([^'.$preg_ec[1].'\.]+)'.$preg_ec[1].'?\./i', + $preg_ec[2].'$1'.$preg_ec[3].'.', $item); + } + } + + return preg_replace('/'.$preg_ec[0].'?([^'.$preg_ec[1].'\.]+)'.$preg_ec[1].'?(\.)?/i', + $preg_ec[2].'$1'.$preg_ec[3].'$2', $item); + } + + //-------------------------------------------------------------------- + + /** + * DB Prefix + * + * Prepends a database prefix if one exists in configuration + * + * @param string $table the table + * + * @return string + * @throws \CodeIgniter\DatabaseException + */ + public function prefixTable($table = '') + { + if ($table === '') + { + throw new DatabaseException('A table name is required for that operation.'); + } + + return $this->DBPrefix.$table; + } + + //-------------------------------------------------------------------- + + /** + * Set DB Prefix + * + * Set's the DB Prefix to something new without needing to reconnect + * + * @param string the prefix + * + * @return string + */ + public function setPrefix($prefix = '') + { + return $this->DBPrefix = $prefix; + } + + //-------------------------------------------------------------------- + + /** + * Returns the total number of rows affected by this query. + * + * @return mixed + */ + abstract public function affectedRows(): int; + + //-------------------------------------------------------------------- + + /** + * "Smart" Escape String + * + * Escapes data based on type. + * Sets boolean and null types + * + * @param $str + * + * @return mixed + */ + public function escape($str) + { + if (is_array($str)) + { + $str = array_map([&$this, 'escape'], $str); + + return $str; + } + else if (is_string($str) OR (is_object($str) && method_exists($str, '__toString'))) + { + return "'".$this->escapeString($str)."'"; + } + else if (is_bool($str)) + { + return ($str === false) ? 0 : 1; + } + else if ($str === null) + { + return 'NULL'; + } + + return $str; + } + + //-------------------------------------------------------------------- + + /** + * Escape String + * + * @param string|string[] $str Input string + * @param bool $like Whether or not the string will be used in a LIKE condition + * @return string + */ + public function escapeString($str, $like = FALSE) + { + if (is_array($str)) + { + foreach ($str as $key => $val) + { + $str[$key] = $this->escapeString($val, $like); + } + + return $str; + } + + $str = $this->_escapeString($str); + + // escape LIKE condition wildcards + if ($like === true) + { + return str_replace( + [$this->likeEscapeChar, '%', '_'], + [$this->likeEscapeChar.$this->likeEscapeChar, $this->likeEscapeChar.'%', $this->likeEscapeChar.'_'], + $str + ); + } + + return $str; + } + + //-------------------------------------------------------------------- + + /** + * Escape LIKE String + * + * Calls the individual driver for platform + * specific escaping for LIKE conditions + * + * @param string|string[] + * @return mixed + */ + public function escapeLikeString($str) + { + return $this->escapeString($str, TRUE); + } + + //-------------------------------------------------------------------- + + + /** + * Platform independent string escape. + * + * Will likely be overridden in child classes. + * + * @param string $str + * + * @return string + */ + protected function _escapeString(string $str): string + { + return str_replace("'", "''", remove_invisible_characters($str)); + } + + //-------------------------------------------------------------------- + + /** + * This function enables you to call PHP database functions that are not natively included + * in CodeIgniter, in a platform independent manner. + * + * @param string $functionName + * @param array ...$params + * + * @return bool + * @throws DatabaseException + */ + public function callFunction(string $functionName, ...$params) + { + $driver = ($this->DBDriver === 'postgre' ? 'pg' : strtolower($this->DBDriver)).'_'; + + if (FALSE === strpos($driver, $functionName)) + { + $functionName = $driver.$functionName; + } + + if ( ! function_exists($functionName)) + { + if ($this->DBDebug) + { + throw new DatabaseException('This feature is not available for the database you are using.'); + } + + return false; + } + + return $functionName(...$params); + } + + //-------------------------------------------------------------------- + + //-------------------------------------------------------------------- + // META Methods + //-------------------------------------------------------------------- + + /** + * Returns an array of table names + * + * @param string $constrain_by_prefix = FALSE + * @return array + */ + public function listTables($constrain_by_prefix = FALSE) + { + // Is there a cached result? + if (isset($this->dataCache['table_names'])) + { + return $this->dataCache['table_names']; + } + + if (FALSE === ($sql = $this->_listTables($constrain_by_prefix))) + { + if ($this->DBDebug) + { + throw new DatabaseException('This feature is not available for the database you are using.'); + } + return false; + } + + $this->dataCache['table_names'] = array(); + $query = $this->query($sql); + + foreach ($query->getResultArray() as $row) + { + // Do we know from which column to get the table name? + if ( ! isset($key)) + { + if (isset($row['table_name'])) + { + $key = 'table_name'; + } + elseif (isset($row['TABLE_NAME'])) + { + $key = 'TABLE_NAME'; + } + else + { + /* We have no other choice but to just get the first element's key. + * Due to array_shift() accepting its argument by reference, if + * E_STRICT is on, this would trigger a warning. So we'll have to + * assign it first. + */ + $key = array_keys($row); + $key = array_shift($key); + } + } + + $this->dataCache['table_names'][] = $row[$key]; + } + + return $this->dataCache['table_names']; + } + + //-------------------------------------------------------------------- + + /** + * Determine if a particular table exists + * + * @param string $table_name + * @return bool + */ + public function tableExists($table_name) + { + return in_array($this->protectIdentifiers($table_name, TRUE, FALSE, FALSE), $this->listTables()); + } + + //-------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * @param string $table Table name + * + * @return array + * @throws DatabaseException + */ + public function getFieldNames($table) + { + // Is there a cached result? + if (isset($this->dataCache['field_names'][$table])) + { + return $this->dataCache['field_names'][$table]; + } + + if (empty($this->connID)) + { + $this->initialize(); + } + + if (FALSE === ($sql = $this->_listColumns($table))) + { + if ($this->DBDebug) + { + throw new DatabaseException('This feature is not available for the database you are using.'); + } + return false; + } + + $query = $this->query($sql); + $this->dataCache['field_names'][$table] = array(); + + foreach ($query->getResultArray() as $row) + { + // Do we know from where to get the column's name? + if ( ! isset($key)) + { + if (isset($row['column_name'])) + { + $key = 'column_name'; + } + elseif (isset($row['COLUMN_NAME'])) + { + $key = 'COLUMN_NAME'; + } + else + { + // We have no other choice but to just get the first element's key. + $key = key($row); + } + } + + $this->dataCache['field_names'][$table][] = $row[$key]; + } + + return $this->dataCache['field_names'][$table]; + } + + //-------------------------------------------------------------------- + + /** + * Determine if a particular field exists + * + * @param string + * @param string + * @return bool + */ + public function fieldExists($fieldName, $tableName) + { + return in_array($fieldName, $this->getFieldNames($tableName)); + } + + //-------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table the table name + * @return array + */ + public function getFieldData(string $table) + { + $query = $this->query($this->_fieldData($this->protect_identifiers($table, TRUE, NULL, FALSE))); + return ($query) ? $query->field_data() : FALSE; + } + + //-------------------------------------------------------------------- + + /** + * Allows the engine to be set into a mode where queries are not + * actually executed, but they are still generated, timed, etc. + * + * This is primarily used by the prepared query functionality. + * + * @param bool $pretend + * + * @return $this + */ + public function pretend(bool $pretend = true) + { + $this->pretend = $pretend; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Returns the last error code and message. + * + * Must return an array with keys 'code' and 'message': + * + * return ['code' => null, 'message' => null); + * + * @return array + */ + abstract public function error(); + + //-------------------------------------------------------------------- + + /** + * Insert ID + * + * @return int + */ + abstract public function insertID(); + + //-------------------------------------------------------------------- + + /** + * Generates the SQL for listing tables in a platform-dependent manner. + * + * @param bool $constrainByPrefix + * + * @return string + */ + abstract protected function _listTables($constrainByPrefix = false): string; + + //-------------------------------------------------------------------- + + /** + * Generates a platform-specific query string so that the column names can be fetched. + * + * @param string $table + * + * @return string + */ + abstract protected function _listColumns(string $table = ''): string; + + //-------------------------------------------------------------------- + + +} diff --git a/ci-4.0-dev/system/Database/BasePreparedQuery.php b/ci-4.0-dev/system/Database/BasePreparedQuery.php new file mode 100644 index 000000000..4c19a962d --- /dev/null +++ b/ci-4.0-dev/system/Database/BasePreparedQuery.php @@ -0,0 +1,216 @@ +db =& $db; + } + + //-------------------------------------------------------------------- + + /** + * Prepares the query against the database, and saves the connection + * info necessary to execute the query later. + * + * NOTE: This version is based on SQL code. Child classes should + * override this method. + * + * @param string $sql + * @param array $options Passed to the connection's prepare statement. + * + * @return mixed + */ + public function prepare(string $sql, array $options = [], $queryClass = 'CodeIgniter\\Database\\Query') + { + // We only supports positional placeholders (?) + // in order to work with the execute method below, so we + // need to replace our named placeholders (:name) + $sql = preg_replace('/:[^\s,)]+/', '?', $sql); + + $query = new $queryClass($this->db); + + $query->setQuery($sql); + + if (! empty($this->db->swapPre) && ! empty($this->db->DBPrefix)) + { + $query->swapPrefix($this->db->DBPrefix, $this->db->swapPre); + } + + $this->query = $query; + + return $this->_prepare($query->getOriginalQuery(), $options); + } + + //-------------------------------------------------------------------- + + /** + * The database-dependent portion of the prepare statement. + * + * @param string $sql + * @param array $options Passed to the connection's prepare statement. + * + * @return mixed + */ + abstract public function _prepare(string $sql, array $options = []); + + //-------------------------------------------------------------------- + + /** + * Takes a new set of data and runs it against the currently + * prepared query. Upon success, will return a Results object. + * + * @param array $data + * + * @return ResultInterface + */ + public function execute(...$data) + { + // Execute the Query. + $startTime = microtime(true); + + $result = $this->_execute($data); + + // Update our query object + $query = clone $this->query; + $query->setBinds($data); + + $query->setDuration($startTime); + + // Let others do something with this query + Hooks::trigger('DBQuery', $query); + + // Return a result object + $resultClass = str_replace('PreparedQuery', 'Result', get_class($this)); + + $resultID = $this->_getResult(); + + return new $resultClass($this->db->connID, $resultID); + } + + //-------------------------------------------------------------------- + + /** + * The database dependant version of the execute method. + * + * @param array $data + * + * @return ResultInterface + */ + abstract public function _execute($data); + + //-------------------------------------------------------------------- + + /** + * Returns the result object for the prepared query. + * + * @return mixed + */ + abstract public function _getResult(); + + //-------------------------------------------------------------------- + + /** + * Explicity closes the statement. + */ + public function close() + { + if (! is_object($this->statement)) + { + return; + } + + $this->statement->close(); + } + + //-------------------------------------------------------------------- + + /** + * Returns the SQL that has been prepared. + * + * @return string + */ + public function getQueryString(): string + { + return $this->sql; + } + + //-------------------------------------------------------------------- + + /** + * A helper to determine if any error exists. + * + * @return bool + */ + public function hasError() + { + return ! empty($this->errorString); + } + + //-------------------------------------------------------------------- + + + /** + * Returns the error code created while executing this statement. + * + * @return string + */ + public function getErrorCode(): int + { + return $this->errorCode; + } + + //-------------------------------------------------------------------- + + /** + * Returns the error message created while executing this statement. + * + * @return string + */ + public function getErrorMessage(): string + { + return $this->errorString; + } + + //-------------------------------------------------------------------- +} diff --git a/ci-4.0-dev/system/Database/BaseResult.php b/ci-4.0-dev/system/Database/BaseResult.php new file mode 100644 index 000000000..8459b9740 --- /dev/null +++ b/ci-4.0-dev/system/Database/BaseResult.php @@ -0,0 +1,620 @@ +connID = $connID; + $this->resultID = $resultID; + } + + //-------------------------------------------------------------------- + + /** + * Retrieve the results of the query. Typically an array of + * individual data rows, which can be either an 'array', an + * 'object', or a custom class name. + * + * @param string $type The row type. Either 'array', 'object', or a class name to use + * + * @return mixed + */ + public function getResult($type = 'object'): array + { + if ($type === 'array') + { + return $this->getResultArray(); + } + elseif ($type === 'object') + { + return $this->getResultObject(); + } + + return $this->getCustomResultObject($type); + } + + //-------------------------------------------------------------------- + + /** + * Returns the results as an array of custom objects. + * + * @param string $className The name of the class to use. + * + * @return mixed + */ + public function getCustomResultObject(string $className) + { + if (isset($this->customResultObject[$className])) + { + return $this->customResultObject[$className]; + } + elseif ( ! $this->resultID OR $this->numRows === 0) + { + return []; + } + + // Don't fetch the result set again if we already have it + $_data = null; + if (($c = count($this->resultArray)) > 0) + { + $_data = 'result_array'; + } + elseif (($c = count($this->resultObject)) > 0) + { + $_data = 'result_object'; + } + + if ($_data !== null) + { + for ($i = 0; $i < $c; $i++) + { + $this->customResultObject[$className][$i] = new $className(); + + foreach ($this->{$_data}[$i] as $key => $value) + { + $this->customResultObject[$className][$i]->$key = $value; + } + } + + return $this->customResultObject[$className]; + } + + is_null($this->rowData) OR $this->dataSeek(0); + $this->customResultObject[$className] = []; + + while ($row = $this->fetchObject($className)) + { + $this->customResultObject[$className][] = $row; + } + + return $this->customResultObject[$className]; + } + + //-------------------------------------------------------------------- + + /** + * Returns the results as an array of arrays. + * + * If no results, an empty array is returned. + * + * @return array + */ + public function getResultArray(): array + { + if (count($this->resultArray) > 0) + { + return $this->resultArray; + } + + // In the event that query caching is on, the result_id variable + // will not be a valid resource so we'll simply return an empty + // array. + if ( ! $this->resultID OR $this->numRows === 0) + { + return []; + } + + if (($c = count($this->resultObject)) > 0) + { + for ($i = 0; $i < $c; $i++) + { + $this->resultArray[$i] = (array)$this->resultObject[$i]; + } + + return $this->resultArray; + } + + is_null($this->rowData) OR $this->dataSeek(0); + while ($row = $this->fetchAssoc()) + { + $this->resultArray[] = $row; + } + + return $this->resultArray; + } + + //-------------------------------------------------------------------- + + /** + * Returns the results as an array of objects. + * + * If no results, an empty array is returned. + * + * @return array + */ + public function getResultObject(): array + { + if (count($this->resultObject) > 0) + { + return $this->resultObject; + } + + // In the event that query caching is on, the result_id variable + // will not be a valid resource so we'll simply return an empty + // array. + if ( ! $this->resultID OR $this->numRows === 0) + { + return []; + } + + if (($c = count($this->resultArray)) > 0) + { + for ($i = 0; $i < $c; $i++) + { + $this->resultObject[$i] = (object)$this->resultArray[$i]; + } + + return $this->resultObject; + } + + is_null($this->rowData) OR $this->dataSeek(0); + while ($row = $this->fetchObject()) + { + $this->resultObject[] = $row; + } + + return $this->resultObject; + } + + //-------------------------------------------------------------------- + + /** + * Wrapper object to return a row as either an array, an object, or + * a custom class. + * + * If row doesn't exist, returns null. + * + * @param int $n The index of the results to return + * @param string $type The type of result object. 'array', 'object' or class name. + * + * @return mixed + */ + public function getRow($n = 0, $type = 'object') + { + if ( ! is_numeric($n)) + { + // We cache the row data for subsequent uses + is_array($this->rowData) OR $this->row_data = $this->getRowArray(0); + + // array_key_exists() instead of isset() to allow for NULL values + if (empty($this->rowData) OR ! array_key_exists($n, $this->rowData)) + { + return null; + } + + return $this->rowData[$n]; + } + + if ($type === 'object') + { + return $this->getRowObject($n); + } + elseif ($type === 'array') + { + return $this->getRowArray($n); + } + else + { + return $this->getCustomRowObject($n, $type); + } + } + + //-------------------------------------------------------------------- + + /** + * Returns a row as a custom class instance. + * + * If row doesn't exists, returns null. + * + * @param int $n + * @param string $className + * + * @return mixed + */ + public function getCustomRowObject($n, string $className) + { + isset($this->customResultObject[$className]) OR $this->customResultObject($className); + + if (count($this->customResultObject[$className]) === 0) + { + return null; + } + + if ($n !== $this->currentRow && isset($this->customResultObject[$className][$n])) + { + $this->current_row = $n; + } + + return $this->customResultObject[$className][$this->currentRow]; + } + + //-------------------------------------------------------------------- + + /** + * Returns a single row from the results as an array. + * + * If row doesn't exist, returns null. + * + * @param int $n + * + * @return mixed + */ + public function getRowArray($n = 0) + { + $result = $this->getResultArray(); + if (count($result) === 0) + { + return null; + } + + if ($n !== $this->currentRow && isset($result[$n])) + { + $this->currentRow = $n; + } + + return $result[$this->currentRow]; + } + + //-------------------------------------------------------------------- + + /** + * Returns a single row from the results as an object. + * + * If row doesn't exist, returns null. + * + * @param int $n + * + * @return mixed + */ + public function getRowObject($n = 0) + { + $result = $this->getResultObject(); + if (count($result) === 0) + { + return null; + } + + if ($n !== $this->customResultObject && isset($result[$n])) + { + $this->currentRow = $n; + } + + return $result[$this->currentRow]; + } + + //-------------------------------------------------------------------- + + /** + * Assigns an item into a particular column slot. + * + * @param $key + * @param null $value + * + * @return mixed + */ + public function setRow($key, $value = null) + { + // We cache the row data for subsequent uses + if ( ! is_array($this->rowData)) + { + $this->row_data = $this->getRowArray(0); + } + + if (is_array($key)) + { + foreach ($key as $k => $v) + { + $this->rowData[$k] = $v; + } + + return; + } + + if ($key !== '' && $value !== null) + { + $this->rowData[$key] = $value; + } + } + + //-------------------------------------------------------------------- + + /** + * Returns the "first" row of the current results. + * + * @param string $type + * + * @return mixed + */ + public function getFirstRow($type = 'object') + { + $result = $this->getResult($type); + + return (count($result) === 0) ? null : $result[0]; + } + + //-------------------------------------------------------------------- + + /** + * Returns the "last" row of the current results. + * + * @param string $type + * + * @return mixed + */ + public function getLastRow($type = 'object') + { + $result = $this->getResult($type); + + return (count($result) === 0) ? null : $result[count($result) - 1]; + } + + //-------------------------------------------------------------------- + + /** + * Returns the "next" row of the current results. + * + * @param string $type + * + * @return mixed + */ + public function getNextRow($type = 'object') + { + $result = $this->getResult($type); + if (count($result) === 0) + { + return null; + } + + return isset($result[$this->currentRow + 1]) + ? $result[++$this->currentRow] + : null; + } + + //-------------------------------------------------------------------- + + /** + * Returns the "previous" row of the current results. + * + * @param string $type + * + * @return mixed + */ + public function getPreviousRow($type = 'object') + { + $result = $this->getResult($type); + if (count($result) === 0) + { + return null; + } + + if (isset($result[$this->currentRow - 1])) + { + --$this->currentRow; + } + + return $result[$this->currentRow]; + } + + //-------------------------------------------------------------------- + + /** + * Returns an unbuffered row and move the pointer to the next row. + * + * @param string $type + * + * @return mixed + */ + public function getUnbufferedRow($type = 'object') + { + if ($type === 'array') + { + return $this->fetchAssoc(); + } + elseif ($type === 'object') + { + return $this->fetchObject(); + } + + return $this->fetchObject($type); + } + + //-------------------------------------------------------------------- + + /** + * Gets the number of fields in the result set. + * + * @return int + */ + abstract public function getFieldCount(): int; + + //-------------------------------------------------------------------- + + /** + * Generates an array of column names in the result set. + * + * @return array + */ + abstract public function getFieldNames(): array; + + //-------------------------------------------------------------------- + + /** + * Generates an array of objects representing field meta-data. + * + * @return array + */ + abstract public function getFieldData(): array; + + //-------------------------------------------------------------------- + + /** + * Frees the current result. + * + * @return mixed + */ + abstract public function freeResult(); + + //-------------------------------------------------------------------- + + /** + * Moves the internal pointer to the desired offset. This is called + * internally before fetching results to make sure the result set + * starts at zero. + * + * @param int $n + * + * @return mixed + */ + abstract public function dataSeek($n = 0); + + //-------------------------------------------------------------------- + + /** + * Returns the result set as an array. + * + * Overridden by driver classes. + * + * @return array + */ + abstract protected function fetchAssoc(); + + //-------------------------------------------------------------------- + + /** + * Returns the result set as an object. + * + * Overridden by child classes. + * + * @param string $className + * + * @return object + */ + abstract protected function fetchObject($className = 'stdClass'); + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Database/BaseUtils.php b/ci-4.0-dev/system/Database/BaseUtils.php new file mode 100644 index 000000000..0461bb84b --- /dev/null +++ b/ci-4.0-dev/system/Database/BaseUtils.php @@ -0,0 +1,438 @@ +db =& $db; + } + + //-------------------------------------------------------------------- + + /** + * List databases + * + * @return array + */ + public function listDatabases() + { + // Is there a cached result? + if (isset($this->db->dataCache['db_names'])) + { + return $this->db->dataCache['db_names']; + } + elseif ($this->listDatabases === FALSE) + { + if ($this->db->DBDebug) + { + throw new DatabaseException('Unsupported feature of the database platform you are using.'); + } + return false; + } + + $this->db->dataCache['db_names'] = array(); + + $query = $this->db->query($this->listDatabases); + if ($query === FALSE) + { + return $this->db->dataCache['db_names']; + } + + for ($i = 0, $query = $query->getResultArray(), $c = count($query); $i < $c; $i++) + { + $this->db->dataCache['db_names'][] = current($query[$i]); + } + + return $this->db->dataCache['db_names']; + } + + //-------------------------------------------------------------------- + + /** + * Determine if a particular database exists + * + * @param string $database_name + * @return bool + */ + public function databaseExists($database_name) + { + return in_array($database_name, $this->listDatabases()); + } + + //-------------------------------------------------------------------- + + /** + * Optimize Table + * + * @param string $table_name + * @return mixed + */ + public function optimizeTable($table_name) + { + if ($this->optimizeTable === FALSE) + { + if ($this->db->DBDebug) + { + throw new DatabaseException('Unsupported feature of the database platform you are using.'); + } + return false; + } + + $query = $this->db->query(sprintf($this->optimizeTable, $this->db->escapeIdentifiers($table_name))); + if ($query !== FALSE) + { + $query = $query->getResultArray(); + return current($query); + } + + return FALSE; + } + + //-------------------------------------------------------------------- + + /** + * Optimize Database + * + * @return mixed + */ + public function optimizeDatabase() + { + if ($this->optimizeTable === FALSE) + { + if ($this->db->DBDebug) + { + throw new DatabaseException('Unsupported feature of the database platform you are using.'); + } + return false; + } + + $result = array(); + foreach ($this->db->listTables() as $table_name) + { + $res = $this->db->query(sprintf($this->optimizeTable, $this->db->escapeIdentifiers($table_name))); + if (is_bool($res)) + { + return $res; + } + + // Build the result array... + $res = $res->getResultArray(); + $res = current($res); + $key = str_replace($this->db->database.'.', '', current($res)); + $keys = array_keys($res); + unset($res[$keys[0]]); + + $result[$key] = $res; + } + + return $result; + } + + //-------------------------------------------------------------------- + + /** + * Repair Table + * + * @param string $table_name + * @return mixed + */ + public function repairTable($table_name) + { + if ($this->repairTable === FALSE) + { + if ($this->db->DBDebug) + { + throw new DatabaseException('Unsupported feature of the database platform you are using.'); + } + return false; + } + + $query = $this->db->query(sprintf($this->repairTable, $this->db->escapeIdentifiers($table_name))); + if (is_bool($query)) + { + return $query; + } + + $query = $query->getResultArray(); + return current($query); + } + + //-------------------------------------------------------------------- + + /** + * Generate CSV from a query result object + * + * @param object $query Query result object + * @param string $delim Delimiter (default: ,) + * @param string $newline Newline character (default: \n) + * @param string $enclosure Enclosure (default: ") + * @return string + */ + public function getCSVFromResult(ResultInterface $query, $delim = ',', $newline = "\n", $enclosure = '"') + { + $out = ''; + // First generate the headings from the table column names + foreach ($query->getFieldNames() as $name) + { + $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $name).$enclosure.$delim; + } + + $out = substr($out, 0, -strlen($delim)).$newline; + + // Next blast through the result array and build out the rows + while ($row = $query->getUnbufferedRow('array')) + { + $line = array(); + foreach ($row as $item) + { + $line[] = $enclosure.str_replace($enclosure, $enclosure.$enclosure, $item).$enclosure; + } + $out .= implode($delim, $line).$newline; + } + + return $out; + } + + //-------------------------------------------------------------------- + + /** + * Generate XML data from a query result object + * + * @param object $query Query result object + * @param array $params Any preferences + * @return string + */ + public function getXMLFromResult(ResultInterface $query, $params = array()) + { + // Set our default values + foreach (array('root' => 'root', 'element' => 'element', 'newline' => "\n", 'tab' => "\t") as $key => $val) + { + if ( ! isset($params[$key])) + { + $params[$key] = $val; + } + } + + // Create variables for convenience + extract($params); + + // Load the xml helper +// get_instance()->load->helper('xml'); + + // Generate the result + $xml = '<'.$root.'>'.$newline; + while ($row = $query->getUnbufferedRow()) + { + $xml .= $tab.'<'.$element.'>'.$newline; + foreach ($row as $key => $val) + { + $xml .= $tab.$tab.'<'.$key.'>'.xml_convert($val).''.$newline; + } + $xml .= $tab.''.$newline; + } + + return $xml.''.$newline; + } + + //-------------------------------------------------------------------- + + /** + * Database Backup + * + * @param array $params + * @return string + */ + public function backup($params = array()) + { + // If the parameters have not been submitted as an + // array then we know that it is simply the table + // name, which is a valid short cut. + if (is_string($params)) + { + $params = array('tables' => $params); + } + + // Set up our default preferences + $prefs = array( + 'tables' => array(), + 'ignore' => array(), + 'filename' => '', + 'format' => 'gzip', // gzip, zip, txt + 'add_drop' => TRUE, + 'add_insert' => TRUE, + 'newline' => "\n", + 'foreign_key_checks' => TRUE + ); + + // Did the user submit any preferences? If so set them.... + if (count($params) > 0) + { + foreach ($prefs as $key => $val) + { + if (isset($params[$key])) + { + $prefs[$key] = $params[$key]; + } + } + } + + // Are we backing up a complete database or individual tables? + // If no table names were submitted we'll fetch the entire table list + if (count($prefs['tables']) === 0) + { + $prefs['tables'] = $this->db->listTables(); + } + + // Validate the format + if ( ! in_array($prefs['format'], array('gzip', 'zip', 'txt'), TRUE)) + { + $prefs['format'] = 'txt'; + } + + // Is the encoder supported? If not, we'll either issue an + // error or use plain text depending on the debug settings + if (($prefs['format'] === 'gzip' && ! function_exists('gzencode')) + OR ($prefs['format'] === 'zip' && ! function_exists('gzcompress'))) + { + if ($this->db->DBDebug) + { + throw new DatabaseException('The file compression format you chose is not supported by your server.'); + } + + $prefs['format'] = 'txt'; + } + + // Was a Zip file requested? + if ($prefs['format'] === 'zip') + { + // Set the filename if not provided (only needed with Zip files) + if ($prefs['filename'] === '') + { + $prefs['filename'] = (count($prefs['tables']) === 1 ? $prefs['tables'] : $this->db->database) + .date('Y-m-d_H-i', time()).'.sql'; + } + else + { + // If they included the .zip file extension we'll remove it + if (preg_match('|.+?\.zip$|', $prefs['filename'])) + { + $prefs['filename'] = str_replace('.zip', '', $prefs['filename']); + } + + // Tack on the ".sql" file extension if needed + if ( ! preg_match('|.+?\.sql$|', $prefs['filename'])) + { + $prefs['filename'] .= '.sql'; + } + } + + // Load the Zip class and output it +// $CI =& get_instance(); +// $CI->load->library('zip'); +// $CI->zip->add_data($prefs['filename'], $this->_backup($prefs)); +// return $CI->zip->get_zip(); + } + elseif ($prefs['format'] === 'txt') // Was a text file requested? + { + return $this->_backup($prefs); + } + elseif ($prefs['format'] === 'gzip') // Was a Gzip file requested? + { + return gzencode($this->_backup($prefs)); + } + + return; + } + + //-------------------------------------------------------------------- + + /** + * Platform dependent version of the backup function. + * + * @param array|null $prefs + * + * @return mixed + */ + abstract public function _backup(array $prefs = null); + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Database/Config.php b/ci-4.0-dev/system/Database/Config.php new file mode 100644 index 000000000..4afec32dd --- /dev/null +++ b/ci-4.0-dev/system/Database/Config.php @@ -0,0 +1,233 @@ +defaultGroup; + } + + if (is_string($group) && ! isset($config->$group) && $group != 'custom') + { + throw new \InvalidArgumentException($group.' is not a valid database connection group.'); + } + + if (isset($config->$group)) + { + $config = $config->$group; + } + + $connection = self::$factory->load($config, $group); + + self::$instances[$group] =& $connection; + + return $connection; + } + + //-------------------------------------------------------------------- + + /** + * Returns an array of all db connections currently made. + * + * @return array + */ + public static function getConnections() + { + return self::$instances; + } + + //-------------------------------------------------------------------- + + /** + * Loads and returns an instance of the Forge for the specified + * database group, and loads the group if it hasn't been loaded yet. + * + * @param string|null $group + */ + public static function forge(string $group = null) + { + $config = new \Config\Database(); + + self::ensureFactory(); + + if (empty($group)) + { + $group = ENVIRONMENT == 'testing' ? 'tests' : $config->defaultGroup; + } + + if (! isset($config->$group)) + { + throw new \InvalidArgumentException($group.' is not a valid database connection group.'); + } + + if (! isset(self::$instances[$group])) + { + $db = self::connect($group); + } + else + { + $db = self::$instances[$group]; + } + + return self::$factory->loadForge($db); + } + + //-------------------------------------------------------------------- + + /** + * Returns a new instance of the Database Utilities class. + * + * @param string|null $group + * + * @return mixed + */ + public static function utils(string $group = null) + { + $config = new \Config\Database(); + + self::ensureFactory(); + + if (empty($group)) + { + $group = $config->defaultGroup; + } + + if (! isset($config->group)) + { + throw new \InvalidArgumentException($group.' is not a valid database connection group.'); + } + + if (! isset(self::$instances[$group])) + { + $db = self::connect($group); + } + else + { + $db = self::$instances[$group]; + } + + return self::$factory->loadUtils($db); + } + + //-------------------------------------------------------------------- + + /** + * Returns a new instance of the Database Seeder. + * + * @param string|null $group + * + * @return Seeder + */ + public static function seeder(string $group = null) + { + $config = new \Config\Database(); + + return new Seeder($config, self::connect($group)); + } + + //-------------------------------------------------------------------- + + + + /** + * Ensures the database Connection Manager/Factory is loaded and ready to use. + */ + protected static function ensureFactory() + { + if (self::$factory instanceof \CodeIgniter\Database\Database) + { + return; + } + + self::$factory = new \CodeIgniter\Database\Database(); + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Database/ConnectionInterface.php b/ci-4.0-dev/system/Database/ConnectionInterface.php new file mode 100644 index 000000000..238216ead --- /dev/null +++ b/ci-4.0-dev/system/Database/ConnectionInterface.php @@ -0,0 +1,222 @@ +connections[$alias] = $class; + + return $this->connections[$alias]; + } + + //-------------------------------------------------------------------- + + /** + * Creates a new Forge instance for the current database type. + * + * @param ConnectionInterface $db + * + * @return mixed + */ + public function loadForge(ConnectionInterface $db) + { + $className = strpos($db->DBDriver, '\\') === false + ? '\CodeIgniter\Database\\'.$db->DBDriver.'\\Forge' + : $db->DBDriver.'\\Connection'; + + // Make sure a connection exists + if (! $db->connID) + { + $db->initialize(); + } + + $class = new $className($db); + + return $class; + } + + //-------------------------------------------------------------------- + + /** + * Loads the Database Utilities class. + * + * @param ConnectionInterface $db + * + * @return mixed + */ + public function loadUtils(ConnectionInterface $db) + { + $className = strpos($db->DBDriver, '\\') === false + ? '\CodeIgniter\Database\\'.$db->DBDriver.'\\Utils' + : $db->DBDriver.'\\Utils'; + + // Make sure a connection exists + if (! $db->connID) + { + $db->initialize(); + } + + $class = new $className($db); + + return $class; + } + + //-------------------------------------------------------------------- + + +} diff --git a/ci-4.0-dev/system/Database/Forge.php b/ci-4.0-dev/system/Database/Forge.php new file mode 100644 index 000000000..afa7d079e --- /dev/null +++ b/ci-4.0-dev/system/Database/Forge.php @@ -0,0 +1,1113 @@ +db =& $db; + } + + //-------------------------------------------------------------------- + + /** + * Provides access to the forge's current database connection. + * + * @return ConnectionInterface + */ + public function getConnection() + { + return $this->db; + } + + //-------------------------------------------------------------------- + + + /** + * Create database + * + * @param string $db_name + * + * @return bool + */ + public function createDatabase($db_name) + { + if ($this->createDatabaseStr === false) + { + if ($this->db->DBDebug) + { + throw new DatabaseException('This feature is not available for the database you are using.'); + } + + return false; + } + elseif ( ! $this->db->query(sprintf($this->createDatabaseStr, $db_name, $this->db->charset, + $this->db->DBCollat)) + ) + { + if ($this->db->DBDebug) + { + throw new DatabaseException('Unable to drop the specified database.'); + } + + return false; + } + + if ( ! empty($this->db->dataCache['db_names'])) + { + $this->db->dataCache['db_names'][] = $db_name; + } + + return true; + } + + //-------------------------------------------------------------------- + + /** + * Drop database + * + * @param string $db_name + * + * @return bool + */ + public function dropDatabase($db_name) + { + if ($this->dropDatabaseStr === false) + { + if ($this->db->DBDebug) + { + throw new DatabaseException('This feature is not available for the database you are using.'); + } + + return false; + } + elseif ( ! $this->db->query(sprintf($this->dropDatabaseStr, $db_name))) + { + if ($this->db->DBDebug) + { + throw new DatabaseException('Unable to drop the specified database.'); + } + + return false; + } + + if ( ! empty($this->db->dataCache['db_names'])) + { + $key = array_search(strtolower($db_name), array_map('strtolower', $this->db->dataCache['db_names']), true); + if ($key !== false) + { + unset($this->db->dataCache['db_names'][$key]); + } + } + + return true; + } + + //-------------------------------------------------------------------- + + /** + * Add Key + * + * @param string $key + * @param bool $primary + * + * @return CI_DB_forge + */ + public function addKey($key, $primary = false) + { + if (is_array($key)) + { + foreach ($key as $one) + { + $this->addKey($one, $primary); + } + + return $this; + } + + if ($primary === true) + { + $this->primaryKeys[] = $key; + } + else + { + $this->keys[] = $key; + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Add Field + * + * @param array $field + * + * @return CI_DB_forge + */ + public function addField($field) + { + if (is_string($field)) + { + if ($field === 'id') + { + $this->addField([ + 'id' => [ + 'type' => 'INT', + 'constraint' => 9, + 'auto_increment' => true, + ], + ]); + $this->addKey('id', true); + } + else + { + if (strpos($field, ' ') === false) + { + throw new \InvalidArgumentException('Field information is required for that operation.'); + } + + $this->fields[] = $field; + } + } + + if (is_array($field)) + { + $this->fields = array_merge($this->fields, $field); + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Create Table + * + * @param string $table Table name + * @param bool $if_not_exists Whether to add IF NOT EXISTS condition + * @param array $attributes Associative array of table attributes + * + * @return bool + * @throws \CodeIgniter\DatabaseException + */ + public function createTable($table, $if_not_exists = false, array $attributes = []) + { + if ($table === '') + { + throw new \InvalidArgumentException('A table name is required for that operation.'); + } + else + { + $table = $this->db->DBPrefix.$table; + } + + if (count($this->fields) === 0) + { + throw new \RuntimeException('Field information is required.'); + } + + $sql = $this->_createTable($table, $if_not_exists, $attributes); + + if (is_bool($sql)) + { + $this->_reset(); + if ($sql === false) + { + if ($this->db->DBDebug) + { + throw new DatabaseException('This feature is not available for the database you are using.'); + } + + return false; + } + } + + if (($result = $this->db->query($sql)) !== false) + { + empty($this->db->dataCache['table_names']) OR $this->db->dataCache['table_names'][] = $table; + + // Most databases don't support creating indexes from within the CREATE TABLE statement + if ( ! empty($this->keys)) + { + for ($i = 0, $sqls = $this->_processIndexes($table), $c = count($sqls); $i < $c; $i++) + { + $this->db->query($sqls[$i]); + } + } + } + + $this->_reset(); + + return $result; + } + + //-------------------------------------------------------------------- + + /** + * Create Table + * + * @param string $table Table name + * @param bool $if_not_exists Whether to add 'IF NOT EXISTS' condition + * @param array $attributes Associative array of table attributes + * + * @return mixed + */ + protected function _createTable($table, $if_not_exists, $attributes) + { + if ($if_not_exists === true && $this->createTableIfStr === false) + { + if ($this->db->tableExists($table)) + { + return true; + } + else + { + $if_not_exists = false; + } + } + + $sql = ($if_not_exists) + ? sprintf($this->createTableIfStr, $this->db->escapeIdentifiers($table)) + : 'CREATE TABLE'; + + $columns = $this->_processFields(true); + for ($i = 0, $c = count($columns); $i < $c; $i++) + { + $columns[$i] = ($columns[$i]['_literal'] !== false) + ? "\n\t".$columns[$i]['_literal'] + : "\n\t".$this->_processColumn($columns[$i]); + } + + $columns = implode(',', $columns) + .$this->_processPrimaryKeys($table); + + // Are indexes created from within the CREATE TABLE statement? (e.g. in MySQL) + if ($this->createTableKeys === true) + { + $columns .= $this->_processIndexes($table); + } + + // createTableStr will usually have the following format: "%s %s (%s\n)" + $sql = sprintf($this->createTableStr.'%s', + $sql, + $this->db->escapeIdentifiers($table), + $columns, + $this->_createTableAttributes($attributes) + ); + + return $sql; + } + + //-------------------------------------------------------------------- + + /** + * CREATE TABLE attributes + * + * @param array $attributes Associative array of table attributes + * + * @return string + */ + protected function _createTableAttributes($attributes) + { + $sql = ''; + + foreach (array_keys($attributes) as $key) + { + if (is_string($key)) + { + $sql .= ' '.strtoupper($key).' '.$attributes[$key]; + } + } + + return $sql; + } + + //-------------------------------------------------------------------- + + /** + * Drop Table + * + * @param string $table_name Table name + * @param bool $if_exists Whether to add an IF EXISTS condition + * + * @return bool + */ + public function dropTable($table_name, $if_exists = false) + { + if ($table_name === '') + { + if ($this->db->DBDebug) + { + throw new DatabaseException('A table name is required for that operation.'); + } + + return false; + } + + if (($query = $this->_dropTable($this->db->DBPrefix.$table_name, $if_exists)) === true) + { + return true; + } + + $query = $this->db->query($query); + + // Update table list cache + if ($query && ! empty($this->db->dataCache['table_names'])) + { + $key = array_search(strtolower($this->db->DBPrefix.$table_name), + array_map('strtolower', $this->db->dataCache['table_names']), true); + if ($key !== false) + { + unset($this->db->dataCache['table_names'][$key]); + } + } + + return $query; + } + + //-------------------------------------------------------------------- + + /** + * Drop Table + * + * Generates a platform-specific DROP TABLE string + * + * @param string $table Table name + * @param bool $if_exists Whether to add an IF EXISTS condition + * + * @return string + */ + protected function _dropTable($table, $if_exists) + { + $sql = 'DROP TABLE'; + + if ($if_exists) + { + if ($this->dropTableIfStr === false) + { + if ( ! $this->db->tableExists($table)) + { + return true; + } + } + else + { + $sql = sprintf($this->dropTableIfStr, $this->db->escapeIdentifiers($table)); + } + } + + return $sql.' '.$this->db->escapeIdentifiers($table); + } + + //-------------------------------------------------------------------- + + /** + * Rename Table + * + * @param string $table_name Old table name + * @param string $new_table_name New table name + * + * @return bool + */ + public function renameTable($table_name, $new_table_name) + { + if ($table_name === '' OR $new_table_name === '') + { + throw new \InvalidArgumentException('A table name is required for that operation.'); + } + elseif ($this->renameTableStr === false) + { + if ($this->db->DBDebug) + { + throw new DatabaseException('This feature is not available for the database you are using.'); + } + + return false; + } + + $result = $this->db->query(sprintf($this->renameTableStr, + $this->db->escapeIdentifiers($this->db->DBPrefix.$table_name), + $this->db->escapeIdentifiers($this->db->DBPrefix.$new_table_name)) + ); + + if ($result && ! empty($this->db->dataCache['table_names'])) + { + $key = array_search(strtolower($this->db->DBPrefix.$table_name), + array_map('strtolower', $this->db->dataCache['table_names']), true); + if ($key !== false) + { + $this->db->dataCache['table_names'][$key] = $this->db->DBPrefix.$new_table_name; + } + } + + return $result; + } + + //-------------------------------------------------------------------- + + /** + * Column Add + * + * @param string $table Table name + * @param array $field Column definition + * @param string $_after Column for AFTER clause (deprecated) + * + * @return bool + */ + public function addColumn($table, $field, $_after = null) + { + // Work-around for literal column definitions + is_array($field) OR $field = [$field]; + + foreach (array_keys($field) as $k) + { + $this->addField([$k => $field[$k]]); + } + + $sqls = $this->_alterTable('ADD', $this->db->DBPrefix.$table, $this->_processFields()); + $this->_reset(); + if ($sqls === false) + { + if ($this->db->DBDebug) + { + throw new DatabaseException('This feature is not available for the database you are using.'); + } + + return false; + } + + for ($i = 0, $c = count($sqls); $i < $c; $i++) + { + if ($this->db->query($sqls[$i]) === false) + { + return false; + } + } + + return true; + } + + //-------------------------------------------------------------------- + + /** + * Column Drop + * + * @param string $table Table name + * @param string $column_name Column name + * + * @return bool + */ + public function dropColumn($table, $column_name) + { + $sql = $this->_alterTable('DROP', $this->db->DBPrefix.$table, $column_name); + if ($sql === false) + { + if ($this->db->DBDebug) + { + throw new DatabaseException('This feature is not available for the database you are using.'); + } + + return false; + } + + return $this->db->query($sql); + } + + //-------------------------------------------------------------------- + + /** + * Column Modify + * + * @param string $table Table name + * @param string $field Column definition + * + * @return bool + */ + public function modifyColumn($table, $field) + { + // Work-around for literal column definitions + is_array($field) OR $field = [$field]; + + foreach (array_keys($field) as $k) + { + $this->addField([$k => $field[$k]]); + } + + if (count($this->fields) === 0) + { + throw new \RuntimeException('Field information is required'); + } + + $sqls = $this->_alterTable('CHANGE', $this->db->DBPrefix.$table, $this->_processFields()); + $this->_reset(); + if ($sqls === false) + { + if ($this->db->DBDebug) + { + throw new DatabaseException('This feature is not available for the database you are using.'); + } + + return false; + } + + for ($i = 0, $c = count($sqls); $i < $c; $i++) + { + if ($this->db->query($sqls[$i]) === false) + { + return false; + } + } + + return true; + } + + //-------------------------------------------------------------------- + + /** + * ALTER TABLE + * + * @param string $alter_type ALTER type + * @param string $table Table name + * @param mixed $field Column definition + * + * @return string|string[] + */ + protected function _alterTable($alter_type, $table, $field) + { + $sql = 'ALTER TABLE '.$this->db->escapeIdentifiers($table).' '; + + // DROP has everything it needs now. + if ($alter_type === 'DROP') + { + return $sql.'DROP COLUMN '.$this->db->escapeIdentifiers($field); + } + + $sql .= ($alter_type === 'ADD') + ? 'ADD ' + : $alter_type.' COLUMN '; + + $sqls = []; + for ($i = 0, $c = count($field); $i < $c; $i++) + { + $sqls[] = $sql + .($field[$i]['_literal'] !== false ? $field[$i]['_literal'] : $this->_processColumn($field[$i])); + } + + return $sqls; + } + + //-------------------------------------------------------------------- + + /** + * Process fields + * + * @param bool $create_table + * + * @return array + */ + protected function _processFields($create_table = false) + { + $fields = []; + + foreach ($this->fields as $key => $attributes) + { + if (is_int($key) && ! is_array($attributes)) + { + $fields[] = ['_literal' => $attributes]; + continue; + } + + $attributes = array_change_key_case($attributes, CASE_UPPER); + + if ($create_table === true && empty($attributes['TYPE'])) + { + continue; + } + + isset($attributes['TYPE']) && $this->_attributeType($attributes); + + $field = [ + 'name' => $key, + 'new_name' => isset($attributes['NAME']) ? $attributes['NAME'] : null, + 'type' => isset($attributes['TYPE']) ? $attributes['TYPE'] : null, + 'length' => '', + 'unsigned' => '', + 'null' => '', + 'unique' => '', + 'default' => '', + 'auto_increment' => '', + '_literal' => false, + ]; + + isset($attributes['TYPE']) && $this->_attributeUnsigned($attributes, $field); + + if ($create_table === false) + { + if (isset($attributes['AFTER'])) + { + $field['after'] = $attributes['AFTER']; + } + elseif (isset($attributes['FIRST'])) + { + $field['first'] = (bool)$attributes['FIRST']; + } + } + + $this->_attributeDefault($attributes, $field); + + if (isset($attributes['NULL'])) + { + if ($attributes['NULL'] === true) + { + $field['null'] = empty($this->null) ? '' : ' '.$this->null; + } + else + { + $field['null'] = ' NOT NULL'; + } + } + elseif ($create_table === true) + { + $field['null'] = ' NOT NULL'; + } + + $this->_attributeAutoIncrement($attributes, $field); + $this->_attributeUnique($attributes, $field); + + if (isset($attributes['COMMENT'])) + { + $field['comment'] = $this->db->escape($attributes['COMMENT']); + } + + if (isset($attributes['TYPE']) && ! empty($attributes['CONSTRAINT'])) + { + switch (strtoupper($attributes['TYPE'])) + { + case 'ENUM': + case 'SET': + $attributes['CONSTRAINT'] = $this->db->escape($attributes['CONSTRAINT']); + $field['length'] = is_array($attributes['CONSTRAINT']) + ? "('".implode("','", $attributes['CONSTRAINT'])."')" + : '('.$attributes['CONSTRAINT'].')'; + break; + default: + $field['length'] = is_array($attributes['CONSTRAINT']) + ? '('.implode(',', $attributes['CONSTRAINT']).')' + : '('.$attributes['CONSTRAINT'].')'; + break; + } + } + + $fields[] = $field; + } + + return $fields; + } + + //-------------------------------------------------------------------- + + /** + * Process column + * + * @param array $field + * + * @return string + */ + protected function _processColumn($field) + { + return $this->db->escapeIdentifiers($field['name']) + .' '.$field['type'].$field['length'] + .$field['unsigned'] + .$field['default'] + .$field['null'] + .$field['auto_increment'] + .$field['unique']; + } + + //-------------------------------------------------------------------- + + /** + * Field attribute TYPE + * + * Performs a data type mapping between different databases. + * + * @param array &$attributes + * + * @return void + */ + protected function _attributeType(&$attributes) + { + // Usually overridden by drivers + } + + //-------------------------------------------------------------------- + + /** + * Field attribute UNSIGNED + * + * Depending on the unsigned property value: + * + * - TRUE will always set $field['unsigned'] to 'UNSIGNED' + * - FALSE will always set $field['unsigned'] to '' + * - array(TYPE) will set $field['unsigned'] to 'UNSIGNED', + * if $attributes['TYPE'] is found in the array + * - array(TYPE => UTYPE) will change $field['type'], + * from TYPE to UTYPE in case of a match + * + * @param array &$attributes + * @param array &$field + * + * @return void + */ + protected function _attributeUnsigned(&$attributes, &$field) + { + if (empty($attributes['UNSIGNED']) OR $attributes['UNSIGNED'] !== true) + { + return; + } + + // Reset the attribute in order to avoid issues if we do type conversion + $attributes['UNSIGNED'] = false; + + if (is_array($this->unsigned)) + { + foreach (array_keys($this->unsigned) as $key) + { + if (is_int($key) && strcasecmp($attributes['TYPE'], $this->unsigned[$key]) === 0) + { + $field['unsigned'] = ' UNSIGNED'; + + return; + } + elseif (is_string($key) && strcasecmp($attributes['TYPE'], $key) === 0) + { + $field['type'] = $key; + + return; + } + } + + return; + } + + $field['unsigned'] = ($this->unsigned === true) ? ' UNSIGNED' : ''; + } + + //-------------------------------------------------------------------- + + /** + * Field attribute DEFAULT + * + * @param array &$attributes + * @param array &$field + * + * @return void + */ + protected function _attributeDefault(&$attributes, &$field) + { + if ($this->default === false) + { + return; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + if ($attributes['DEFAULT'] === null) + { + $field['default'] = empty($this->null) ? '' : $this->default.$this->null; + + // Override the NULL attribute if that's our default + $attributes['NULL'] = true; + $field['null'] = empty($this->null) ? '' : ' '.$this->null; + } + else + { + $field['default'] = $this->default.$this->db->escape($attributes['DEFAULT']); + } + } + } + + //-------------------------------------------------------------------- + + /** + * Field attribute UNIQUE + * + * @param array &$attributes + * @param array &$field + * + * @return void + */ + protected function _attributeUnique(&$attributes, &$field) + { + if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === true) + { + $field['unique'] = ' UNIQUE'; + } + } + + //-------------------------------------------------------------------- + + /** + * Field attribute AUTO_INCREMENT + * + * @param array &$attributes + * @param array &$field + * + * @return void + */ + protected function _attributeAutoIncrement(&$attributes, &$field) + { + if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === true && + stripos($field['type'], 'int') !== false + ) + { + $field['auto_increment'] = ' AUTO_INCREMENT'; + } + } + + //-------------------------------------------------------------------- + + /** + * Process primary keys + * + * @param string $table Table name + * + * @return string + */ + protected function _processPrimaryKeys($table) + { + $sql = ''; + + for ($i = 0, $c = count($this->primaryKeys); $i < $c; $i++) + { + if ( ! isset($this->fields[$this->primaryKeys[$i]])) + { + unset($this->primaryKeys[$i]); + } + } + + if (count($this->primaryKeys) > 0) + { + $sql .= ",\n\tCONSTRAINT ".$this->db->escapeIdentifiers('pk_'.$table) + .' PRIMARY KEY('.implode(', ', $this->db->escapeIdentifiers($this->primaryKeys)).')'; + } + + return $sql; + } + + //-------------------------------------------------------------------- + + /** + * Process indexes + * + * @param string $table + * + * @return string + */ + protected function _processIndexes($table) + { + $sqls = []; + + for ($i = 0, $c = count($this->keys); $i < $c; $i++) + { + if (is_array($this->keys[$i])) + { + for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++) + { + if ( ! isset($this->fields[$this->keys[$i][$i2]])) + { + unset($this->keys[$i][$i2]); + continue; + } + } + } + elseif ( ! isset($this->fields[$this->keys[$i]])) + { + unset($this->keys[$i]); + continue; + } + + is_array($this->keys[$i]) OR $this->keys[$i] = [$this->keys[$i]]; + + $sqls[] = 'CREATE INDEX '.$this->db->escapeIdentifiers($table.'_'.implode('_', $this->keys[$i])) + .' ON '.$this->db->escapeIdentifiers($table) + .' ('.implode(', ', $this->db->escapeIdentifiers($this->keys[$i])).');'; + } + + return $sqls; + } + + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + + /** + * Reset + * + * Resets table creation vars + * + * @return void + */ + protected function _reset() + { + $this->fields = $this->keys = $this->primaryKeys = []; + } + +} diff --git a/ci-4.0-dev/system/Database/Migration.php b/ci-4.0-dev/system/Database/Migration.php new file mode 100644 index 000000000..5ac601c09 --- /dev/null +++ b/ci-4.0-dev/system/Database/Migration.php @@ -0,0 +1,106 @@ +forge = ! is_null($forge) + ? $forge + : \Config\Database::forge($this->DBGroup); + + $this->db = $this->forge->getConnection(); + } + + //-------------------------------------------------------------------- + + /** + * Returns the database group name this migration uses. + * + * @return string + */ + public function getDBGroup() + { + return $this->DBGroup; + } + + //-------------------------------------------------------------------- + + /** + * Perform a migration step. + */ + abstract public function up(); + + //-------------------------------------------------------------------- + + /** + * Revert a migration step. + */ + abstract public function down(); + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Database/MigrationRunner.php b/ci-4.0-dev/system/Database/MigrationRunner.php new file mode 100644 index 000000000..e9c9119c6 --- /dev/null +++ b/ci-4.0-dev/system/Database/MigrationRunner.php @@ -0,0 +1,534 @@ +enabled = $config->enabled ?? false; + $this->type = $config->type ?? 'timestamp'; + $this->table = $config->table ?? 'migrations'; + $this->currentVersion = $config->currentVersion ?? 0; + + if (empty($this->table)) + { + throw new ConfigException(lang('Migrations.migMissingTable')); + } + + if ( ! in_array($this->type, ['sequential', 'timestamp'])) + { + throw new ConfigException(lang('Migrations.migInvalidType').$this->type); + } + + // Migration basename regex + $this->regex = ($this->type === 'timestamp') + ? '/^\d{14}_(\w+)$/' + : '/^\d{3}_(\w+)$/'; + + // If no db connection passed in, use + // default database group. + $this->db = ! empty($db) + ? $db + : \Config\Database::connect(); + + $this->ensureTable(); + } + + //-------------------------------------------------------------------- + + /** + * Migrate to a schema version + * + * Calls each migration step required to get to the schema version of + * choice + * + * @param string $targetVersion Target schema version + * @param $group + * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure + * @throws ConfigException + */ + public function version(string $targetVersion, $group='default') + { + if (! $this->enabled) + { + throw new ConfigException(lang('Migrations.migDisabled')); + } + + // Note: We use strings, so that timestamp versions work on 32-bit systems + $currentVersion = $this->getVersion($group); + + $migrations = $this->findMigrations(); + + if ($targetVersion > 0 && ! isset($migrations[$targetVersion])) + { + throw new \RuntimeException(lang('Migrations.migNotFound').$targetVersion); + } + + if ($targetVersion > $currentVersion) + { + // Moving Up + $method = 'up'; + } + else + { + // Moving Down, apply in reverse order + $method = 'down'; + krsort($migrations); + } + + if (empty($migrations)) + { + return true; + } + + $previous = false; + + // Validate all available migrations, and run the ones within our target range + foreach ($migrations as $number => $file) + { + // Check for sequence gaps + if ($this->type === 'sequential' && $previous !== false && abs($number - $previous) > 1) + { + throw new \RuntimeException(lang('Migration.migGap').$number); + } + + include_once $file; + $class = 'Migration_'.($this->getMigrationName(basename($file, '.php'))); + + // Validate the migration file structure + if ( ! class_exists($class, false)) + { + throw new \RuntimeException(sprintf(lang('Migrations.migClassNotFound'), $class)); + } + + $previous = $number; + + // Run migrations that are inside the target range + if ( + ($method === 'up' && $number > $currentVersion && $number <= $targetVersion) OR + ($method === 'down' && $number <= $currentVersion && $number > $targetVersion) + ) + { + $instance = new $class(); + + if ( ! is_callable([$instance, $method])) + { + throw new \RuntimeException(sprintf(lang('Migrations.migMissingMethod'), $method)); + } + + $instance->{$method}(); + + $currentVersion = $number; + if ($method === 'up') $this->addHistory(basename($file, '.php'), $instance->getDBGroup()); + elseif ($method === 'down') $this->removeHistory(basename($file, '.php'), $instance->getDBGroup()); + } + } + + return $currentVersion; + } + + //-------------------------------------------------------------------- + + /** + * Sets the schema to the latest migration + * + * @return mixed Current version string on success, FALSE on failure + */ + public function latest() + { + $migrations = $this->findMigrations(); + + if (empty($migrations)) + { + if ($this->silent) return false; + + throw new \RuntimeException(lang('Migrations.migNotFound')); + } + + $lastMigration = basename(end($migrations), '.php'); + + // Calculate the last migration step from existing migration + // filenames and proceed to the standard version migration + return $this->version($lastMigration); + } + + //-------------------------------------------------------------------- + + /** + * Sets the schema to the migration version set in config + * + * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure + */ + public function current() + { + return $this->version($this->currentVersion); + } + + //-------------------------------------------------------------------- + + /** + * Retrieves list of available migration scripts + * + * @return array list of migration file paths sorted by version + */ + public function findMigrations() + { + $migrations = []; + + // If $path has been set, ONLY do that one since + // the user has specified their will. Otherwise, + // scan all PSR4 paths. This is required so + // when tests are ran, it will only look in it's + // location. + $paths = []; + if (empty($this->path)) + { + $config = new Autoload(); + foreach ($config->psr4 as $dir) + { + $paths[] = rtrim($dir, '/').'/Database/Migrations/'; + } + } + else + { + $paths[] = $this->path; + } + + // Loop through all of our namespaced folders + // searching for migration directories. + foreach ($paths as $dir) + { + if (! is_dir($dir)) + { + continue; + } + + // Load all *_*.php files in the migrations path + foreach (glob($dir.'*_*.php') as $file) + { + $name = basename($file, '.php'); + + // Filter out non-migration files + if (preg_match($this->regex, $name)) + { + $migrations[$name] = $file; + } + } + } + + ksort($migrations); + + return $migrations; + } + + //-------------------------------------------------------------------- + + /** + * Updates the expected location of the migration files. + * Allows other scripts to modify on the fly as needed. + * + * @param string $path + * + * @return $this + */ + public function setPath(string $path) + { + $this->path = rtrim($path, '/').'/'; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Grabs the full migration history from the database. + * + * @param $group + * @return mixed + */ + public function getHistory($group = 'default') + { + $query = $this->db->table($this->table) + ->where('group', $group) + ->get(); + + if (! $query) return []; + + return $query->getResultArray(); + } + + //-------------------------------------------------------------------- + + /** + * If $silent == true, then will not throw exceptions and will + * attempt to continue gracefully. + * + * @param bool $silent + * + * @return $this + */ + public function setSilent(bool $silent) + { + $this->silent = $silent; + + return $this; + } + + //-------------------------------------------------------------------- + + + + /** + * Extracts the migration number from a filename + * + * @param string $migration + * + * @return string Numeric portion of a migration filename + */ + protected function getMigrationNumber($migration) + { + return sscanf($migration, '%[0-9]+', $number) + ? $number : '0'; + } + + //-------------------------------------------------------------------- + + /** + * Extracts the migration class name from a filename + * + * @param string $migration + * + * @return string text portion of a migration filename + */ + protected function getMigrationName($migration) + { + $parts = explode('_', $migration); + array_shift($parts); + + return implode('_', $parts); + } + + //-------------------------------------------------------------------- + + /** + * Retrieves current schema version + * + * @param $group + * @return string Current migration version + */ + protected function getVersion($group = 'default') + { + if (empty($group)) + { + $config = new \Config\Database(); + $group = $config->defaultGroup; + unset($config); + } + + $row = $this->db->table($this->table) + ->select('version') + ->where('group', $group) + ->orderBy('version', 'DESC') + ->get() + ->getRow(); + + return $row ? $row->version : '0'; + } + + //-------------------------------------------------------------------- + + /** + * Stores the current schema version. + * + * @param string $version + * @param string $group The database group + * + * @internal param string $migration Migration reached + * + */ + protected function addHistory($version, $group = 'default') + { + if (empty($group)) + { + $config = new \Config\Database(); + $group = $config->defaultGroup; + unset($config); + } + + $this->db->table($this->table) + ->insert([ + 'version' => $version, + 'group' => $group, + 'time' => time() + ]); + } + + //-------------------------------------------------------------------- + + /** + * Removes a single history + * + * @param string $version + * @param string $group The database group + */ + protected function removeHistory($version, $group = 'default') + { + if (empty($group)) + { + $config = new \Config\Database(); + $group = $config->defaultGroup; + unset($config); + } + + $this->db->table($this->table) + ->where('version', $version) + ->where('group', $group) + ->delete(); + } + + //-------------------------------------------------------------------- + + /** + * Ensures that we have created our migrations table + * in the database. + */ + protected function ensureTable() + { + if ($this->db->tableExists($this->table)) + { + return; + } + + $forge = \Config\Database::forge(); + + $forge->addField([ + 'version' => [ + 'type' => 'VARCHAR', + 'constraint' => 255, + 'null' => false + ], + 'group' => [ + 'type' => 'VARCHAR', + 'constraint' => 255, + 'null' => false + ], + 'time' => [ + 'type' => 'INT', + 'constraint' => 11, + 'null' => false + ] + ]); + + $forge->createTable($this->table, true); + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Database/MySQLi/Builder.php b/ci-4.0-dev/system/Database/MySQLi/Builder.php new file mode 100644 index 000000000..c0eabf403 --- /dev/null +++ b/ci-4.0-dev/system/Database/MySQLi/Builder.php @@ -0,0 +1,52 @@ +hostname[0] === '/') + { + $hostname = null; + $port = null; + $socket = $this->hostname; + } + else + { + $hostname = ($persistent === true) + ? 'p:'.$this->hostname + : $this->hostname; + $port = empty($this->port) ? null : $this->port; + $socket = null; + } + + $client_flags = ($this->compress === true) ? MYSQLI_CLIENT_COMPRESS : 0; + $this->mysqli = mysqli_init(); + + $this->mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 10); + + if (isset($this->strictOn)) + { + if ($this->strictOn) + { + $this->mysqli->options(MYSQLI_INIT_COMMAND, + 'SET SESSION sql_mode = CONCAT(@@sql_mode, ",", "STRICT_ALL_TABLES")'); + } + else + { + $this->mysqli->options(MYSQLI_INIT_COMMAND, + 'SET SESSION sql_mode = + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE( + @@sql_mode, + "STRICT_ALL_TABLES,", ""), + ",STRICT_ALL_TABLES", ""), + "STRICT_ALL_TABLES", ""), + "STRICT_TRANS_TABLES,", ""), + ",STRICT_TRANS_TABLES", ""), + "STRICT_TRANS_TABLES", "")' + ); + } + } + + if (is_array($this->encrypt)) + { + $ssl = []; + empty($this->encrypt['ssl_key']) OR $ssl['key'] = $this->encrypt['ssl_key']; + empty($this->encrypt['ssl_cert']) OR $ssl['cert'] = $this->encrypt['ssl_cert']; + empty($this->encrypt['ssl_ca']) OR $ssl['ca'] = $this->encrypt['ssl_ca']; + empty($this->encrypt['ssl_capath']) OR $ssl['capath'] = $this->encrypt['ssl_capath']; + empty($this->encrypt['ssl_cipher']) OR $ssl['cipher'] = $this->encrypt['ssl_cipher']; + + if ( ! empty($ssl)) + { + if (isset($this->encrypt['ssl_verify'])) + { + if ($this->encrypt['ssl_verify']) + { + defined('MYSQLI_OPT_SSL_VERIFY_SERVER_CERT') && + $this->mysqli->options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, true); + } + // Apparently (when it exists), setting MYSQLI_OPT_SSL_VERIFY_SERVER_CERT + // to FALSE didn't do anything, so PHP 5.6.16 introduced yet another + // constant ... + // + // https://secure.php.net/ChangeLog-5.php#5.6.16 + // https://bugs.php.net/bug.php?id=68344 + elseif (defined('MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT')) + { + $this->mysqli->options(MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT, true); + } + } + + $client_flags |= MYSQLI_CLIENT_SSL; + $this->mysqli->ssl_set( + isset($ssl['key']) ? $ssl['key'] : null, + isset($ssl['cert']) ? $ssl['cert'] : null, + isset($ssl['ca']) ? $ssl['ca'] : null, + isset($ssl['capath']) ? $ssl['capath'] : null, + isset($ssl['cipher']) ? $ssl['cipher'] : null + ); + } + } + + if ($this->mysqli->real_connect($hostname, $this->username, $this->password, $this->database, $port, $socket, + $client_flags) + ) + { + // Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails + if ( + ($client_flags & MYSQLI_CLIENT_SSL) + && version_compare($this->mysqli->client_info, '5.7.3', '<=') + && empty($this->mysqli->query("SHOW STATUS LIKE 'ssl_cipher'") + ->fetch_object()->Value) + ) + { + $this->mysqli->close(); + $message = 'MySQLi was configured for an SSL connection, but got an unencrypted connection instead!'; + log_message('error', $message); + + if ($this->DBDebug) + { + throw new DatabaseException($message); + } + return false; + } + + if ( ! $this->mysqli->set_charset($this->charset)) + { + log_message('error', "Database: Unable to set the configured connection charset ('{$this->charset}')."); + $this->mysqli->close(); + + if ($this->db->debug) + { + throw new DatabaseException('Unable to set client connection character set: '.$this->charset); + } + return false; + } + + return $this->mysqli; + } + + return false; + } + + //-------------------------------------------------------------------- + + /** + * Keep or establish the connection if no queries have been sent for + * a length of time exceeding the server's idle timeout. + * + * @return mixed + */ + public function reconnect() + { + if ($this->connID !== false && $this->connID->ping() === false) + { + $this->connID = false; + } + } + + //-------------------------------------------------------------------- + + /** + * Close the database connection. + */ + protected function _close() + { + $this->connID->close(); + } + + //-------------------------------------------------------------------- + + /** + * Select a specific database table to use. + * + * @param string $databaseName + * + * @return mixed + */ + public function setDatabase(string $databaseName) + { + if ($databaseName === '') + { + $databaseName = $this->database; + } + + if ($this->connID->select_db($databaseName)) + { + $this->database = $databaseName; + + return true; + } + + return false; + } + + //-------------------------------------------------------------------- + + /** + * Returns a string containing the version of the database being used. + * + * @return mixed + */ + public function getVersion() + { + if (isset($this->dataCache['version'])) + { + return $this->dataCache['version']; + } + + if (empty($this->mysqli)) + { + $this->initialize(); + } + + return $this->dataCache['version'] = $this->mysqli->server_info; + } + + //-------------------------------------------------------------------- + + /** + * Executes the query against the database. + * + * @param $sql + * + * @return mixed + */ + public function execute($sql) + { + return $this->connID->query($this->prepQuery($sql)); + } + + //-------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @param string $sql an SQL query + * + * @return string + */ + protected function prepQuery($sql) + { + // mysqli_affected_rows() returns 0 for "DELETE FROM TABLE" queries. This hack + // modifies the query so that it a proper number of affected rows is returned. + if ($this->deleteHack === true && preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql)) + { + return trim($sql).' WHERE 1=1'; + } + + return $sql; + } + + //-------------------------------------------------------------------- + + /** + * Returns the total number of rows affected by this query. + * + * @return mixed + */ + public function affectedRows(): int + { + return $this->connID->affected_rows; + } + + //-------------------------------------------------------------------- + + /** + * Platform-dependant string escape + * + * @param string $str + * @return string + */ + protected function _escapeString(string $str): string + { + if (is_bool($str)) + { + return $str; + } + + return $this->connID->real_escape_string($str); + } + + //-------------------------------------------------------------------- + + /** + * Generates the SQL for listing tables in a platform-dependent manner. + * + * @param bool $prefixLimit + * + * @return string + */ + protected function _listTables($prefixLimit = false): string + { + $sql = 'SHOW TABLES FROM '.$this->escapeIdentifiers($this->database); + + if ($prefixLimit !== FALSE && $this->DBPrefix !== '') + { + return $sql." LIKE '".$this->escapeLikeStr($this->DBPrefix)."%'"; + } + + return $sql; + } + + //-------------------------------------------------------------------- + + /** + * Generates a platform-specific query string so that the column names can be fetched. + * + * @param string $table + * + * @return string + */ + protected function _listColumns(string $table = ''): string + { + return 'SHOW COLUMNS FROM '.$this->protectIdentifiers($table, TRUE, NULL, FALSE); + } + + //-------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function fieldData(string $table) + { + if (($query = $this->query('SHOW COLUMNS FROM '.$this->protectIdentifiers($table, TRUE, NULL, FALSE))) === FALSE) + { + return FALSE; + } + $query = $query->getResultObject(); + + $retval = array(); + for ($i = 0, $c = count($query); $i < $c; $i++) + { + $retval[$i] = new \stdClass(); + $retval[$i]->name = $query[$i]->Field; + + sscanf($query[$i]->Type, '%[a-z](%d)', + $retval[$i]->type, + $retval[$i]->max_length + ); + + $retval[$i]->default = $query[$i]->Default; + $retval[$i]->primary_key = (int) ($query[$i]->Key === 'PRI'); + } + + return $retval; + } + + //-------------------------------------------------------------------- + + /** + * Returns the last error code and message. + * + * Must return an array with keys 'code' and 'message': + * + * return ['code' => null, 'message' => null); + * + * @return array + */ + public function error() + { + if ( ! empty($this->mysqli->connect_errno)) + { + return array( + 'code' => $this->mysqli->connect_errno, + 'message' => $this->_mysqli->connect_error + ); + } + + return array('code' => $this->connID->errno, 'message' => $this->connID->error); + } + + //-------------------------------------------------------------------- + + /** + * Insert ID + * + * @return int + */ + public function insertID() + { + return $this->connID->insert_id; + } + + //-------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + protected function _transBegin():bool + { + $this->connID->autocommit(false); + + return $this->connID->begin_transaction(); + } + + //-------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + protected function _transCommit(): bool + { + if ($this->connID->commit()) + { + $this->connID->autocommit(true); + return true; + } + + return false; + } + + //-------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + protected function _transRollback(): bool + { + if ($this->connID->rollback()) + { + $this->connID->autocommit(true); + return true; + } + + return false; + } + + //-------------------------------------------------------------------- +} diff --git a/ci-4.0-dev/system/Database/MySQLi/Forge.php b/ci-4.0-dev/system/Database/MySQLi/Forge.php new file mode 100644 index 000000000..1c310ed72 --- /dev/null +++ b/ci-4.0-dev/system/Database/MySQLi/Forge.php @@ -0,0 +1,239 @@ +db->charset) && ! strpos($sql, 'CHARACTER SET') && ! strpos($sql, 'CHARSET')) + { + $sql .= ' DEFAULT CHARACTER SET = '.$this->db->charset; + } + + if ( ! empty($this->db->DBCollat) && ! strpos($sql, 'COLLATE')) + { + $sql .= ' COLLATE = '.$this->db->DBCollat; + } + + return $sql; + } + + //-------------------------------------------------------------------- + + /** + * ALTER TABLE + * + * @param string $alter_type ALTER type + * @param string $table Table name + * @param mixed $field Column definition + * @return string|string[] + */ + protected function _alterTable($alter_type, $table, $field) + { + if ($alter_type === 'DROP') + { + return parent::_alterTable($alter_type, $table, $field); + } + + $sql = 'ALTER TABLE '.$this->db->escapeIdentifiers($table); + for ($i = 0, $c = count($field); $i < $c; $i++) + { + if ($field[$i]['_literal'] !== FALSE) + { + $field[$i] = ($alter_type === 'ADD') + ? "\n\tADD ".$field[$i]['_literal'] + : "\n\tMODIFY ".$field[$i]['_literal']; + } + else + { + if ($alter_type === 'ADD') + { + $field[$i]['_literal'] = "\n\tADD "; + } + else + { + $field[$i]['_literal'] = empty($field[$i]['new_name']) ? "\n\tMODIFY " : "\n\tCHANGE "; + } + + $field[$i] = $field[$i]['_literal'].$this->_processColumn($field[$i]); + } + } + + return array($sql.implode(',', $field)); + } + + //-------------------------------------------------------------------- + + /** + * Process column + * + * @param array $field + * @return string + */ + protected function _processColumn($field) + { + $extra_clause = isset($field['after']) + ? ' AFTER '.$this->db->escapeIdentifiers($field['after']) : ''; + + if (empty($extra_clause) && isset($field['first']) && $field['first'] === TRUE) + { + $extra_clause = ' FIRST'; + } + + return $this->db->escapeIdentifiers($field['name']) + .(empty($field['new_name']) ? '' : ' '.$this->db->escapeIdentifiers($field['new_name'])) + .' '.$field['type'].$field['length'] + .$field['unsigned'] + .$field['null'] + .$field['default'] + .$field['auto_increment'] + .$field['unique'] + .(empty($field['comment']) ? '' : ' COMMENT '.$field['comment']) + .$extra_clause; + } + + //-------------------------------------------------------------------- + + /** + * Process indexes + * + * @param string $table (ignored) + * @return string + */ + protected function _processIndexes($table) + { + $sql = ''; + + for ($i = 0, $c = count($this->keys); $i < $c; $i++) + { + if (is_array($this->keys[$i])) + { + for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++) + { + if ( ! isset($this->fields[$this->keys[$i][$i2]])) + { + unset($this->keys[$i][$i2]); + continue; + } + } + } + elseif ( ! isset($this->fields[$this->keys[$i]])) + { + unset($this->keys[$i]); + continue; + } + + is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]); + + $sql .= ",\n\tKEY ".$this->db->escapeIdentifiers(implode('_', $this->keys[$i])) + .' ('.implode(', ', $this->db->escapeIdentifiers($this->keys[$i])).')'; + } + + $this->keys = array(); + + return $sql; + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Database/MySQLi/PreparedQuery.php b/ci-4.0-dev/system/Database/MySQLi/PreparedQuery.php new file mode 100644 index 000000000..88a1e171d --- /dev/null +++ b/ci-4.0-dev/system/Database/MySQLi/PreparedQuery.php @@ -0,0 +1,95 @@ +sql = rtrim($sql, ';'); + + if (! $this->statement = $this->db->mysqli->prepare($this->sql)) + { + $this->errorCode = $this->db->mysqli->errno; + $this->errorString = $this->db->mysqli->error; + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Takes a new set of data and runs it against the currently + * prepared query. Upon success, will return a Results object. + * + * @param array $data + * + * @return ResultInterface + */ + public function _execute($data) + { + if (is_null($this->statement)) + { + throw new \BadMethodCallException('You must call prepare before trying to execute a prepared statement.'); + } + + // First off -bind the parameters + $bindTypes = ''; + + // Determine the type string + foreach ($data as $item) + { + if (is_integer($item)) + { + $bindTypes .= 'i'; + } + elseif (is_numeric($item)) + { + $bindTypes .= 'd'; + } + else + { + $bindTypes .= 's'; + } + } + + // Bind it + $this->statement->bind_param($bindTypes, ...$data); + + $success = $this->statement->execute(); + + return $success; + } + + //-------------------------------------------------------------------- + + /** + * Returns the result object for the prepared query. + * + * @return mixed + */ + public function _getResult() + { + return $this->statement->get_result(); + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Database/MySQLi/Result.php b/ci-4.0-dev/system/Database/MySQLi/Result.php new file mode 100644 index 000000000..989d24c3e --- /dev/null +++ b/ci-4.0-dev/system/Database/MySQLi/Result.php @@ -0,0 +1,164 @@ +resultID->field_count; + } + + //-------------------------------------------------------------------- + + /** + * Generates an array of column names in the result set. + * + * @return array + */ + public function getFieldNames(): array + { + $fieldNames = []; + $this->resultID->field_seek(0); + while ($field = $this->resultID->fetch_field()) + { + $fieldNames[] = $field->name; + } + + return $fieldNames; + } + + //-------------------------------------------------------------------- + + /** + * Generates an array of objects representing field meta-data. + * + * @return array + */ + public function getFieldData(): array + { + $retval = []; + $fieldData = $this->resultID->fetch_fields(); + + for ($i = 0, $c = count($fieldData); $i < $c; $i++) + { + $retval[$i] = new \stdClass(); + $retval[$i]->name = $fieldData[$i]->name; + $retval[$i]->type = $fieldData[$i]->type; + $retval[$i]->max_length = $fieldData[$i]->max_length; + $retval[$i]->primary_key = (int)($fieldData[$i]->flags & 2); + $retval[$i]->default = $fieldData[$i]->def; + } + + return $retval; + } + + //-------------------------------------------------------------------- + + /** + * Frees the current result. + * + * @return mixed + */ + public function freeResult() + { + if (is_object($this->resultID)) + { + $this->resultID->free(); + $this->resultID = false; + } + } + + //-------------------------------------------------------------------- + + /** + * Moves the internal pointer to the desired offset. This is called + * internally before fetching results to make sure the result set + * starts at zero. + * + * @param int $n + * + * @return mixed + */ + public function dataSeek($n = 0) + { + return $this->resultID->data_seek($n); + } + + //-------------------------------------------------------------------- + + /** + * Returns the result set as an array. + * + * Overridden by driver classes. + * + * @return array + */ + protected function fetchAssoc() + { + return $this->resultID->fetch_assoc(); + } + + //-------------------------------------------------------------------- + + /** + * Returns the result set as an object. + * + * Overridden by child classes. + * + * @param string $className + * + * @return object + */ + protected function fetchObject($className = 'stdClass') + { + return $this->resultID->fetch_object($className); + } + + //-------------------------------------------------------------------- +} diff --git a/ci-4.0-dev/system/Database/Postgre/Builder.php b/ci-4.0-dev/system/Database/Postgre/Builder.php new file mode 100644 index 000000000..19ba0389c --- /dev/null +++ b/ci-4.0-dev/system/Database/Postgre/Builder.php @@ -0,0 +1,362 @@ + 1 ? "0.{$orderby}" : $orderby); + } + + if (is_float($orderby)) + { + $this->db->simpleQuery("SET SEED {$orderby}"); + } + + $orderby = $this->randomKeyword[0]; + $direction = ''; + $escape = false; + } + + return parent::orderBy($orderby, $direction, $escape); + } + + //-------------------------------------------------------------------- + + /** + * Increments a numeric column by the specified value. + * + * @param string $column + * @param int $value + * + * @return bool + */ + public function increment(string $column, int $value = 1) + { + $column = $this->db->protectIdentifiers($column); + + $sql = $this->_update($this->QBFrom[0], [$column => "to_number({$column}, '9999999') + {$value}"]); + + return $this->db->query($sql, $this->binds); + } + + //-------------------------------------------------------------------- + + /** + * Decrements a numeric column by the specified value. + * + * @param string $column + * @param int $value + * + * @return bool + */ + public function decrement(string $column, int $value = 1) + { + $column = $this->db->protectIdentifiers($column); + + $sql = $this->_update($this->QBFrom[0], [$column => "to_number({$column}, '9999999') - {$value}"]); + + return $this->db->query($sql, $this->binds); + } + + //-------------------------------------------------------------------- + + /** + * Replace + * + * Compiles an replace into string and runs the query. + * Because PostgreSQL doesn't support the replace into command, + * we simply do a DELETE and an INSERT on the first key/value + * combo, assuming that it's either the primary key or a unique key. + * + * @param array an associative array of insert values + * @param bool $returnSQL + * + * @return bool TRUE on success, FALSE on failure + * @throws DatabaseException + * @internal param true $bool returns the generated SQL, false executes the query. + * + */ + public function replace($set = null, $returnSQL = false) + { + if ($set !== null) + { + $this->set($set); + } + + if (count($this->QBSet) === 0) + { + if (CI_DEBUG) + { + throw new DatabaseException('You must use the "set" method to update an entry.'); + } + return false; + } + + $table = $this->QBFrom[0]; + + $set = $this->binds; + $keys = array_keys($set); + $values = array_values($set); + + $builder = $this->db->table($table); + $exists = $builder->where("$keys[0] = $values[0]", null, false)->get()->getFirstRow(); + + if (empty($exists)) + { + $result = $builder->insert($set, false); + } + else + { + array_pop($set); + $result = $builder->update($set, "$keys[0] = $values[0]"); + } + + unset($builder); + $this->resetWrite(); + + return $result; + } + + //-------------------------------------------------------------------- + + /** + * Delete + * + * Compiles a delete string and runs the query + * + * @param string $where + * @param null $limit + * @param bool $reset_data + * @param bool $returnSQL + * + * @return mixed + * @throws DatabaseException + * @internal param the $mixed where clause + * @internal param the $mixed limit clause + * @internal param $bool + * + */ + public function delete($where = '', $limit = null, $reset_data = true, $returnSQL = false) + { + if (! empty($limit) || ! empty($this->QBLimit)) + { + throw new DatabaseException('PostgreSQL does not allow LIMITs on DELETE queries.'); + } + + return parent::delete($where, $limit, $reset_data, $returnSQL); + } + + //-------------------------------------------------------------------- + + /** + * LIMIT string + * + * Generates a platform-specific LIMIT clause. + * + * @param string $sql SQL Query + * + * @return string + */ + protected function _limit($sql) + { + return $sql.' LIMIT '.$this->QBLimit.($this->QBOffset ? " OFFSET {$this->QBOffset}" : ''); + } + + //-------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @param $table + * @param $values + * + * @return string + * @throws DatabaseException + * @internal param the $string table name + * @internal param the $array update data + * + */ + protected function _update($table, $values) + { + if (! empty($this->QBLimit)) + { + throw new DatabaseException('Postgres does not support LIMITs with UPDATE queries.'); + } + + $this->QBOrderBy = []; + return parent::_update($table, $values); + } + + //-------------------------------------------------------------------- + + /** + * Update_Batch statement + * + * Generates a platform-specific batch update string from the supplied data + * + * @param string $table Table name + * @param array $values Update data + * @param string $index WHERE key + * + * @return string + */ + protected function _updateBatch($table, $values, $index) + { + $ids = []; + foreach ($values as $key => $val) + { + $ids[] = $val[$index]; + + foreach (array_keys($val) as $field) + { + if ($field !== $index) + { + $final[$field][] = "WHEN {$val[$index]} THEN {$val[$field]}"; + } + } + } + + $cases = ''; + foreach ($final as $k => $v) + { + $cases .= "{$k} = (CASE {$index}\n" + .implode("\n", $v) + ."\nELSE {$k} END), "; + } + + $this->where("{$index} IN(".implode(',', $ids).')', null, false); + + return "UPDATE {$table} SET ".substr($cases, 0, -2).$this->compileWhereHaving('QBWhere'); + } + + //-------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @param string the table name + * + * @return string + */ + protected function _delete($table) + { + $this->QBLimit = false; + return parent::_delete($table); + } + + //-------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * + * If the database does not support the truncate() command, + * then this method maps to 'DELETE FROM table' + * + * @param string the table name + * + * @return string + */ + protected function _truncate($table) + { + return 'TRUNCATE '.$table.' RESTART IDENTITY'; + } + + //-------------------------------------------------------------------- + + /** + * Platform independent LIKE statement builder. + * + * In PostgreSQL, the ILIKE operator will perform case insensitive + * searches according to the current locale. + * + * @see https://www.postgresql.org/docs/9.2/static/functions-matching.html + * + * @param string|null $prefix + * @param string $column + * @param string|null $not + * @param string $bind + * @param bool $insensitiveSearch + * + * @return string $like_statement + */ + public function _like_statement(string $prefix=null, string $column, string $not = null, string $bind, bool $insensitiveSearch=false): string + { + $op = $insensitiveSearch === true ? 'ILIKE' : 'LIKE'; + + return "{$prefix} {$column} {$not} {$op} :{$bind}"; + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Database/Postgre/Connection.php b/ci-4.0-dev/system/Database/Postgre/Connection.php new file mode 100644 index 000000000..94359642e --- /dev/null +++ b/ci-4.0-dev/system/Database/Postgre/Connection.php @@ -0,0 +1,489 @@ +DSN)) + { + $this->buildDSN(); + } + + // Strip pgsql if exists + if (mb_strpos($this->DSN, 'pgsql:') === 0) + { + $this->DSN = mb_substr($this->DSN, 6); + } + + // Convert semicolons to spaces. + $this->DSN = str_replace(';', ' ', $this->DSN); + + $this->connID = $persistent === true + ? pg_pconnect($this->DSN) + : pg_connect($this->DSN); + + if ($this->connID !== false) + { + if ($persistent === true + && pg_connection_status($this->connID) === PGSQL_CONNECTION_BAD + && pg_ping($this->connID) === false + ) + { + return false; + } + + empty($this->schema) or $this->simpleQuery("SET search_path TO {$this->schema},public"); + + if ($this->setClientEncoding($this->charset) === false) + { + return false; + } + } + + return $this->connID; + } + + //-------------------------------------------------------------------- + + /** + * Keep or establish the connection if no queries have been sent for + * a length of time exceeding the server's idle timeout. + * + * @return mixed + */ + public function reconnect() + { + if (pg_ping($this->connID) === false) + { + $this->connID = false; + } + } + + //-------------------------------------------------------------------- + + /** + * Close the database connection. + */ + protected function _close() + { + pg_close($this->connID); + } + + //-------------------------------------------------------------------- + + /** + * Select a specific database table to use. + * + * @param string $databaseName + * + * @return mixed + */ + public function setDatabase(string $databaseName) + { + return false; + } + + //-------------------------------------------------------------------- + + /** + * Returns a string containing the version of the database being used. + * + * @return mixed + */ + public function getVersion() + { + if (isset($this->dataCache['version'])) + { + return $this->dataCache['version']; + } + + if ( ! $this->connID or ($pgVersion = pg_version($this->connID)) === false) + { + $this->initialize(); + } + + return isset($pgVersion['server']) + ? $this->dataCache['version'] = $pgVersion['server'] : false; + } + + //-------------------------------------------------------------------- + + /** + * Executes the query against the database. + * + * @param $sql + * + * @return mixed + */ + public function execute($sql) + { + return pg_query($this->connID, $sql); + } + + //-------------------------------------------------------------------- + + /** + * Returns the total number of rows affected by this query. + * + * @return mixed + */ + public function affectedRows(): int + { + return pg_affected_rows($this->resultID); + } + + //-------------------------------------------------------------------- + + /** + * "Smart" Escape String + * + * Escapes data based on type + * + * @param string $str + * @return mixed + */ + public function escape($str) + { + if (is_string($str) OR (is_object($str) && method_exists($str, '__toString'))) { + return pg_escape_literal($this->connID, $str); + } + elseif (is_bool($str)) + { + return $str ? 'TRUE' : 'FALSE'; + } + + return parent::escape($str); + } + + //-------------------------------------------------------------------- + + /** + * Platform-dependant string escape + * + * @param string $str + * @return string + */ + protected function _escapeString(string $str): string + { + return pg_escape_string($this->connID, $str); + } + + //-------------------------------------------------------------------- + + /** + * Generates the SQL for listing tables in a platform-dependent manner. + * + * @param bool $prefixLimit + * + * @return string + */ + protected function _listTables($prefixLimit = false): string + { + $sql = 'SELECT "table_name" FROM "information_schema"."tables" WHERE "table_schema" = \''.$this->schema."'"; + + if ($prefixLimit !== false && $this->DBPrefix !== '') + { + return $sql.' AND "table_name" LIKE \'' + .$this->escapeLikeString($this->DBPrefix)."%' " + .sprintf($this->likeEscapeStr, $this->likeEscapeChar); + } + + return $sql; + } + + //-------------------------------------------------------------------- + + /** + * Generates a platform-specific query string so that the column names can be fetched. + * + * @param string $table + * + * @return string + */ + protected function _listColumns(string $table = ''): string + { + return 'SELECT "column_name" + FROM "information_schema"."columns" + WHERE LOWER("table_name") = ' + .$this->escape(strtolower($table)); + } + + //-------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @param string $table + * @return array + */ + public function fieldData(string $table) + { + $sql = 'SELECT "column_name", "data_type", "character_maximum_length", "numeric_precision", "column_default" + FROM "information_schema"."columns" + WHERE LOWER("table_name") = ' + .$this->escape(strtolower($table)); + + if (($query = $this->query($sql)) === false) + { + return false; + } + $query = $query->getResultObject(); + + $retval = []; + for ($i = 0, $c = count($query); $i < $c; $i++) + { + $retval[$i] = new \stdClass(); + $retval[$i]->name = $query[$i]->column_name; + $retval[$i]->type = $query[$i]->data_type; + $retval[$i]->default = $query[$i]->column_default; + $retval[$i]->max_length = $query[$i]->character_maximum_length > 0 + ? $query[$i]->character_maximum_length + : $query[$i]->numeric_precision; + } + + return $retval; + } + + //-------------------------------------------------------------------- + + /** + * Returns the last error code and message. + * + * Must return an array with keys 'code' and 'message': + * + * return ['code' => null, 'message' => null); + * + * @return array + */ + public function error() + { + return [ + 'code' => '', + 'message' => pg_last_error($this->connID) + ]; + } + + //-------------------------------------------------------------------- + + /** + * Insert ID + * + * @return int + */ + public function insertID() + { + $v = pg_version($this->connID); + // 'server' key is only available since PostgreSQL 7.4 + $v = isset($v['server']) ? $v['server'] : 0; + + $table = func_num_args() > 0 ? func_get_arg(0) : null; + $column = func_num_args() > 1 ? func_get_arg(1) : null; + + if ($table === null && $v >= '8.1') + { + $sql = 'SELECT LASTVAL() AS ins_id'; + } + elseif ($table !== null) + { + if ($column !== null && $v >= '8.0') + { + $sql = "SELECT pg_get_serial_sequence('{$table}', '{$column}') AS seq"; + $query = $this->query($sql); + $query = $query->row(); + $seq = $query->seq; + } + else + { + // seq_name passed in table parameter + $seq = $table; + } + + $sql = "SELECT CURRVAL('{$seq}') AS ins_id"; + } + else + { + return pg_last_oid($this->resultID); + } + + $query = $this->query($sql); + $query = $query->getRow(); + return (int) $query->ins_id; + } + + //-------------------------------------------------------------------- + + /** + * Build a DSN from the provided parameters + * + * @return void + */ + protected function buildDSN() + { + $this->DSN === '' or $this->DSN = ''; + + // If UNIX sockets are used, we shouldn't set a port + if (strpos($this->hostname, '/') !== false) + { + $this->port = ''; + } + + $this->hostname === '' or $this->DSN = "host={$this->hostname} "; + + if ( ! empty($this->port) && ctype_digit($this->port)) + { + $this->DSN .= "port={$this->port} "; + } + + if ($this->username !== '') + { + $this->DSN .= "user={$this->username} "; + + // An empty password is valid! + // password must be set to null to ignore it. + + $this->password === null or $this->DSN .= "password='{$this->password}' "; + } + + $this->database === '' or $this->DSN .= "dbname={$this->database} "; + + // We don't have these options as elements in our standard configuration + // array, but they might be set by parse_url() if the configuration was + // provided via string> Example: + // + // postgre://username:password@localhost:5432/database?connect_timeout=5&sslmode=1 + foreach (['connect_timeout', 'options', 'sslmode', 'service'] as $key) + { + if (isset($this->{$key}) && is_string($this->{$key}) && $this->{$key} !== '') + { + $this->DSN .= "{$key}='{$this->{$key}}' "; + } + } + + $this->DSN = rtrim($this->DSN); + } + + //-------------------------------------------------------------------- + + /** + * Set client encoding + * + * @param string $charset The client encoding to which the data will be converted. + * @return bool + */ + protected function setClientEncoding($charset) + { + return pg_set_client_encoding($this->connID, $charset) === 0; + } + + //-------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @return bool + */ + protected function _transBegin(): bool + { + return (bool)pg_query($this->connID, 'BEGIN'); + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + protected function _transCommit(): bool + { + return (bool)pg_query($this->connID, 'COMMIT'); + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + protected function _transRollback(): bool + { + return (bool)pg_query($this->connID, 'ROLLBACK'); + } + + // -------------------------------------------------------------------- +} diff --git a/ci-4.0-dev/system/Database/Postgre/Forge.php b/ci-4.0-dev/system/Database/Postgre/Forge.php new file mode 100644 index 000000000..24f500591 --- /dev/null +++ b/ci-4.0-dev/system/Database/Postgre/Forge.php @@ -0,0 +1,185 @@ + 'INTEGER', + 'SMALLINT' => 'INTEGER', + 'INT' => 'BIGINT', + 'INT4' => 'BIGINT', + 'INTEGER' => 'BIGINT', + 'INT8' => 'NUMERIC', + 'BIGINT' => 'NUMERIC', + 'REAL' => 'DOUBLE PRECISION', + 'FLOAT' => 'DOUBLE PRECISION' + ]; + + /** + * NULL value representation in CREATE/ALTER TABLE statements + * + * @var string + */ + protected $_null = 'NULL'; + + //-------------------------------------------------------------------- + + /** + * ALTER TABLE + * + * @param string $alter_type ALTER type + * @param string $table Table name + * @param mixed $field Column definition + * + * @return string|string[] + */ + protected function _alterTable($alter_type, $table, $field) + { + if (in_array($alter_type, ['DROP', 'ADD'], true)) + { + return parent::_alterTable($alter_type, $table, $field); + } + + $sql = 'ALTER TABLE '.$this->db->escapeIdentifiers($table); + $sqls = []; + for ($i = 0, $c = count($field); $i < $c; $i++) + { + if ($field[$i]['_literal'] !== false) + { + return false; + } + + if (version_compare($this->db->getVersion(), '8', '>=') && isset($field[$i]['type'])) + { + $sqls[] = $sql.' ALTER COLUMN '.$this->db->escapeIdentifiers($field[$i]['name']) + ." TYPE {$field[$i]['type']}{$field[$i]['length']}"; + } + + if ( ! empty($field[$i]['default'])) + { + $sqls[] = $sql.' ALTER COLUMN '.$this->db->escapeIdentifiers($field[$i]['name']) + ." SET DEFAULT {$field[$i]['default']}"; + } + + if (isset($field[$i]['null'])) + { + $sqls[] = $sql.' ALTER COLUMN '.$this->db->escapeIdentifiers($field[$i]['name']) + .($field[$i]['null'] === true ? ' DROP' : ' SET'). ' NOT NULL'; + } + + if ( ! empty($field[$i]['new_name'])) + { + $sqls[] = $sql.' RENAME COLUMN '.$this->db->escapeIdentifiers($field[$i]['name']) + .' TO '.$this->db->escapeIdentifiers($field[$i]['new_name']); + } + + if ( ! empty($field[$i]['comment'])) + { + $sqls[] = 'COMMENT ON COLUMN'.$this->db->escapeIdentifiers($table) + .'.'.$this->db->escapeIdentifiers($field[$i]['name']) + ." IS {$field[$i]['comment']}"; + } + } + + return $sqls; + } + + //-------------------------------------------------------------------- + + /** + * Field attribute TYPE + * + * Performs a data type mapping between different databases. + * + * @param array &$attributes + * + * @return void + */ + protected function _attributeType(&$attributes) + { + // Reset field lengths for data types that don't support it + if (isset($attributes['CONSTRAINT']) && stripos($attributes['TYPE'], 'int') !== false) + { + $attributes['CONSTRAINT'] = null; + } + + switch (strtoupper($attributes['TYPE'])) + { + case 'TINYINT': + $attributes['TYPE'] = 'SMALLINT'; + $attributes['UNSIGNED'] = false; + return; + case 'MEDIUMINT': + $attributes['TYPE'] = 'INTEGER'; + $attributes['UNSIGNED'] = false; + return; + case 'DATETIME': + $attributes['TYPE'] = 'TIMESTAMP'; + default: + return; + } + } + + //-------------------------------------------------------------------- + + /** + * Field attribute AUTO_INCREMENT + * + * @param array &$attributes + * @param array &$field + * + * @return void + */ + protected function _attributeAutoIncrement(&$attributes, &$field) + { + if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === true) + { + $field['type'] = $field['type'] === 'NUMERIC' ? 'BIGSERIAL' : 'SERIAL'; + } + } + + //-------------------------------------------------------------------- +} diff --git a/ci-4.0-dev/system/Database/Postgre/PreparedQuery.php b/ci-4.0-dev/system/Database/Postgre/PreparedQuery.php new file mode 100644 index 000000000..ee2aa4374 --- /dev/null +++ b/ci-4.0-dev/system/Database/Postgre/PreparedQuery.php @@ -0,0 +1,112 @@ +name = mt_rand(1, 10000000000000000); + + $this->sql = $this->parameterize($sql); + + if (! $this->statement = pg_prepare($this->db->connID, $this->name, $this->sql)) + { + $this->errorCode = 0; + $this->errorString = pg_last_error($this->db->connID); + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Takes a new set of data and runs it against the currently + * prepared query. Upon success, will return a Results object. + * + * @param array $data + * + * @return ResultInterface + */ + public function _execute($data) + { + if (is_null($this->statement)) + { + throw new \BadMethodCallException('You must call prepare before trying to execute a prepared statement.'); + } + + $this->result = pg_execute($this->db->connID, $this->name, $data); + + return (bool)$this->result; + } + + //-------------------------------------------------------------------- + + /** + * Returns the result object for the prepared query. + * + * @return mixed + */ + public function _getResult() + { + return $this->result; + } + + //-------------------------------------------------------------------- + + /** + * Replaces the ? placeholders with $1, $2, etc parameters for use + * within the prepared query. + * + * @param string $sql + * + * @return string + */ + public function parameterize(string $sql): string + { + // Track our current value + $count = 0; + + $sql = preg_replace_callback('/\?/', function($matches) use (&$count){ + $count++; + return "\${$count}"; + }, $sql); + + return $sql; + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Database/Postgre/Result.php b/ci-4.0-dev/system/Database/Postgre/Result.php new file mode 100644 index 000000000..ad19aa828 --- /dev/null +++ b/ci-4.0-dev/system/Database/Postgre/Result.php @@ -0,0 +1,162 @@ +resultID); + } + + //-------------------------------------------------------------------- + + /** + * Generates an array of column names in the result set. + * + * @return array + */ + public function getFieldNames(): array + { + $fieldNames = []; + for ($i = 0, $c = $this->getFieldCount(); $i < $c; $i++) + { + $fieldNames[] = pg_field_name($this->resultID, $i); + } + + return $fieldNames; + } + + //-------------------------------------------------------------------- + + /** + * Generates an array of objects representing field meta-data. + * + * @return array + */ + public function getFieldData(): array + { + $retval = []; + + for ($i = 0, $c = $this->getFieldCount(); $i < $c; $i++) + { + $retval[$i] = new \stdClass(); + $retval[$i]->name = pg_field_name($this->resultID, $i); + $retval[$i]->type = pg_field_type($this->resultID, $i); + $retval[$i]->max_length = pg_field_size($this->resultID, $i); + // $retval[$i]->primary_key = (int)($fieldData[$i]->flags & 2); + // $retval[$i]->default = $fieldData[$i]->def; + } + + return $retval; + } + + //-------------------------------------------------------------------- + + /** + * Frees the current result. + * + * @return mixed + */ + public function freeResult() + { + if (is_resource($this->resultID)) + { + pg_free_result($this->resultID); + $this->resultID = false; + } + } + + //-------------------------------------------------------------------- + + /** + * Moves the internal pointer to the desired offset. This is called + * internally before fetching results to make sure the result set + * starts at zero. + * + * @param int $n + * + * @return mixed + */ + public function dataSeek($n = 0) + { + return pg_result_seek($this->resultID, $n); + } + + //-------------------------------------------------------------------- + + /** + * Returns the result set as an array. + * + * Overridden by driver classes. + * + * @return array + */ + protected function fetchAssoc() + { + return pg_fetch_assoc($this->resultID); + } + + //-------------------------------------------------------------------- + + /** + * Returns the result set as an object. + * + * Overridden by child classes. + * + * @param string $className + * + * @return object + */ + protected function fetchObject($className = 'stdClass') + { + return pg_fetch_object($this->resultID, null, $className); + } + + //-------------------------------------------------------------------- +} diff --git a/ci-4.0-dev/system/Database/Postgre/Utils.php b/ci-4.0-dev/system/Database/Postgre/Utils.php new file mode 100644 index 000000000..4a02dd41b --- /dev/null +++ b/ci-4.0-dev/system/Database/Postgre/Utils.php @@ -0,0 +1,73 @@ +db = $db; + } + + //-------------------------------------------------------------------- + + + /** + * Sets the raw query string to use for this statement. + * + * @param string $sql + * @param array $binds + * + * @return mixed + */ + public function setQuery(string $sql, $binds=null) + { + $this->originalQueryString = $sql; + + if (! is_null($binds)) + { + $this->binds = $binds; + } + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Will store the variables to bind into the query later. + * + * @param array $binds + * + * @return $this + */ + public function setBinds(array $binds) + { + $this->binds = $binds; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Returns the final, processed query string after binding, etal + * has been performed. + * + * @return mixed + */ + public function getQuery(): string + { + if (empty($this->finalQueryString)) + { + $this->finalQueryString = $this->originalQueryString; + } + + $this->compileBinds(); + + return $this->finalQueryString; + } + + //-------------------------------------------------------------------- + + /** + * Records the execution time of the statement using microtime(true) + * for it's start and end values. If no end value is present, will + * use the current time to determine total duration. + * + * @param int $start + * @param int|null $end + * + * @return mixed + */ + public function setDuration(float $start, float $end = null) + { + $this->startTime = $start; + + if (is_null($end)) + { + $end = microtime(true); + } + + $this->endTime = $end; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Returns the start time in seconds with microseconds. + * + * @param bool $returnRaw + * @param int $decimals + * + * @return mixed + */ + public function getStartTime($returnRaw = false, int $decimals = 6) + { + if ($returnRaw) + { + return $this->startTime; + } + + return number_format($this->startTime, $decimals); + } + + //-------------------------------------------------------------------- + /** + * Returns the duration of this query during execution, or null if + * the query has not been executed yet. + * + * @param int $decimals The accuracy of the returned time. + * + * @return mixed + */ + public function getDuration(int $decimals = 6) + { + return number_format(($this->endTime - $this->startTime), $decimals); + } + + //-------------------------------------------------------------------- + + /** + * Stores the error description that happened for this query. + * + * @param int $code + * @param string $error + */ + public function setError(int $code, string $error) + { + $this->errorCode = $code; + $this->errorString = $error; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Reports whether this statement created an error not. + * + * @return bool + */ + public function hasError(): bool + { + return ! empty($this->errorString); + } + + //-------------------------------------------------------------------- + + /** + * Returns the error code created while executing this statement. + * + * @return string + */ + public function getErrorCode(): int + { + return $this->errorCode; + } + + //-------------------------------------------------------------------- + + /** + * Returns the error message created while executing this statement. + * + * @return string + */ + public function getErrorMessage(): string + { + return $this->errorString; + } + + //-------------------------------------------------------------------- + + /** + * Determines if the statement is a write-type query or not. + * + * @return bool + */ + public function isWriteType(): bool + { + return (bool)preg_match( + '/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX)\s/i', + $this->originalQueryString); + } + + //-------------------------------------------------------------------- + + /** + * Swaps out one table prefix for a new one. + * + * @param string $orig + * @param string $swap + * + * @return mixed + */ + public function swapPrefix(string $orig, string $swap) + { + $sql = empty($this->finalQueryString) ? $this->originalQueryString : $this->finalQueryString; + + $this->finalQueryString = preg_replace('/(\W)'.$orig.'(\S+?)/', '\\1'.$swap.'\\2', $sql); + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Returns the original SQL that was passed into the system. + * + * @return string + */ + public function getOriginalQuery() + { + return $this->originalQueryString; + } + + //-------------------------------------------------------------------- + + /** + * Escapes and inserts any binds into the finalQueryString object. + */ + protected function compileBinds() + { + $sql = $this->finalQueryString; + + $hasNamedBinds = strpos($sql, ':') !== false; + + if (empty($this->binds) || empty($this->bindMarker) || + (strpos($sql, $this->bindMarker) === false && + $hasNamedBinds === false) + ) + { + return; + } + + if ( ! is_array($this->binds)) + { + $binds = [$this->binds]; + $bindCount = 1; + } + else + { + $binds = $this->binds; + $bindCount = count($binds); + } + + // Reverse the binds so that duplicate named binds + // will be processed prior to the original binds. + if (! is_numeric(key(array_slice($binds, 0, 1)))) + { + $binds = array_reverse($binds); + } + + // We'll need marker length later + $ml = strlen($this->bindMarker); + + if ($hasNamedBinds) + { + $sql = $this->matchNamedBinds($sql, $binds); + } + else + { + $sql = $this->matchSimpleBinds($sql, $binds, $bindCount, $ml); + } + + $this->finalQueryString = $sql; + } + + //-------------------------------------------------------------------- + + /** + * Match bindings + * @param string $sql + * @param array $binds + * @return string + */ + protected function matchNamedBinds(string $sql, array $binds) + { + foreach ($binds as $placeholder => $value) + { + $escapedValue = $this->db->escape($value); + + // In order to correctly handle backlashes in saved strings + // we will need to preg_quote, so remove the wrapping escape characters + // otherwise it will get escaped. + if (is_array($value)) + { + foreach ($value as &$item) + { + $item = preg_quote($item); + } + + $escapedValue = '('.implode(',', $escapedValue).')'; + } + else + { + $escapedValue = strpos($escapedValue, '\\') !== false + ? preg_quote(trim($escapedValue, $this->db->escapeChar)) + : $escapedValue; + } + + $sql = preg_replace('/:'.$placeholder.'(?!\w)/', $escapedValue, $sql); + } + + return $sql; + } + + //-------------------------------------------------------------------- + + /** + * Match bindings + * @param string $sql + * @param array $binds + * @param int $bindCount + * @param int $ml + * @return string + */ + protected function matchSimpleBinds(string $sql, array $binds, int $bindCount, int $ml) + { + // Make sure not to replace a chunk inside a string that happens to match the bind marker + if ($c = preg_match_all("/'[^']*'/i", $sql, $matches)) + { + $c = preg_match_all('/'.preg_quote($this->bindMarker, '/').'/i', + str_replace($matches[0], + str_replace($this->bindMarker, str_repeat(' ', $ml), $matches[0]), + $sql, $c), + $matches, PREG_OFFSET_CAPTURE); + + // Bind values' count must match the count of markers in the query + if ($bindCount !== $c) + { + return $sql; + } + } + // Number of binds must match bindMarkers in the string. + else if (($c = preg_match_all('/'.preg_quote($this->bindMarker, '/').'/i', $sql, $matches, + PREG_OFFSET_CAPTURE)) !== $bindCount) + { + return $sql; + } + + do + { + $c--; + $escapedValue = $this->db->escape($binds[$c]); + if (is_array($escapedValue)) + { + $escapedValue = '('.implode(',', $escapedValue).')'; + } + $sql = substr_replace($sql, $escapedValue, $matches[0][$c][1], $ml); + } + while ($c !== 0); + + return $sql; + } + + //-------------------------------------------------------------------- + + /** + * Return text representation of the query + * + * @return type + */ + public function __toString() + { + return $this->getQuery(); + } + + //-------------------------------------------------------------------- + + +} diff --git a/ci-4.0-dev/system/Database/QueryInterface.php b/ci-4.0-dev/system/Database/QueryInterface.php new file mode 100644 index 000000000..58d0bb589 --- /dev/null +++ b/ci-4.0-dev/system/Database/QueryInterface.php @@ -0,0 +1,155 @@ +seedPath = $config->filesPath ?? APPPATH.'Database/'; + + if (empty($this->seedPath)) + { + throw new \InvalidArgumentException('Invalid filesPath set in the Config\Database.'); + } + + $this->seedPath = rtrim($this->seedPath, '/').'/Seeds/'; + + if (! is_dir($this->seedPath)) + { + throw new \InvalidArgumentException('Unable to locate the seeds directory. Please check Config\Database::filesPath'); + } + + $this->config =& $config; + + if (is_null($db)) + { + $db = \Config\Database::connect($this->DBGroup); + } + + $this->db =& $db; + } + + //-------------------------------------------------------------------- + + /** + * Loads the specified seeder and runs it. + * + * @param string $class + * + * @throws RuntimeException + */ + public function call(string $class) + { + if (empty($class)) + { + throw new \InvalidArgumentException('No Seeder was specified.'); + } + + $path = str_replace('.php', '', $class).'.php'; + + // If we have namespaced class, simply try to load it. + if (strpos($class, '\\') !== false) + { + $seeder = new $class($this->config); + } + // Otherwise, try to load the class manually. + else + { + $path = $this->seedPath.$path; + + if (! is_file($path)) + { + throw new \InvalidArgumentException('The specified Seeder is not a valid file: '. $path); + } + + if (! class_exists($class, false)) + { + require $path; + } + + $seeder = new $class($this->config); + } + + $seeder->run(); + + unset($seeder); + + if (is_cli() && ! $this->silent) + { + CLI::write("Seeded: {$class}", 'green'); + } + } + + //-------------------------------------------------------------------- + + /** + * Sets the location of the directory that seed files can be located in. + * + * @param sting $path + * + * @return $this + */ + public function setPath(string $path) + { + $this->seedPath = rtrim($path, '/').'/'; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Sets the silent treatment. + * + * @param bool $silent + * + * @return $this + */ + public function setSilent(bool $silent) + { + $this->silent = $silent; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Run the database seeds. This is where the magic happens. + * + * Child classes must implement this method and take care + * of inserting their data here. + * + * @return mixed + */ + public function run() + { + + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Debug/CustomExceptions.php b/ci-4.0-dev/system/Debug/CustomExceptions.php new file mode 100644 index 000000000..acfac4bdb --- /dev/null +++ b/ci-4.0-dev/system/Debug/CustomExceptions.php @@ -0,0 +1,181 @@ +ob_level = ob_get_level(); + + $this->viewPath = rtrim($config->errorViewPath, '/ ').'/'; + } + + //-------------------------------------------------------------------- + + /** + * Responsible for registering the error, exception and shutdown + * handling of our application. + */ + public function initialize() + { + //Set the Exception Handler + set_exception_handler([$this, 'exceptionHandler']); + + // Set the Error Handler + set_error_handler([$this, 'errorHandler']); + + // Set the handler for shutdown to catch Parse errors + // Do we need this in PHP7? + register_shutdown_function([$this, 'shutdownHandler']); + } + + //-------------------------------------------------------------------- + + /** + * Catches any uncaught errors and exceptions, including most Fatal errors + * (Yay PHP7!). Will log the error, display it if display_errors is on, + * and fire an event that allows custom actions to be taken at this point. + * + * @param \Throwable $exception + */ + public function exceptionHandler(\Throwable $exception) + { + // Get Exception Info - these are available + // directly in the template that's displayed. + $type = get_class($exception); + $codes = $this->determineCodes($exception); + $code = $codes[0]; + $exit = $codes[1]; + $code = $exception->getCode(); + $message = $exception->getMessage(); + $file = $exception->getFile(); + $line = $exception->getLine(); + $trace = $exception->getTrace(); + $title = get_class($exception); + + if (empty($message)) + { + $message = '(null)'; + } + + // Log it + + // Fire an Event + $templates_path = $this->viewPath; + if (empty($templates_path)) + { + $templates_path = APPPATH.'Views/errors/'; + } + + if (is_cli()) + { + $templates_path .= 'cli/'; + } + else + { + header('HTTP/1.1 500 Internal Server Error', true, 500); + $templates_path .= 'html/'; + } + + $view = $this->determineView($exception, $templates_path); + + if (ob_get_level() > $this->ob_level + 1) + { + ob_end_clean(); + } + + ob_start(); + include($templates_path.$view); + $buffer = ob_get_contents(); + ob_end_clean(); + echo $buffer; + + exit($exit); + } + + //-------------------------------------------------------------------- + + /** + * Even in PHP7, some errors make it through to the errorHandler, so + * convert these to Exceptions and let the exception handler log it and + * display it. + * + * This seems to be primarily when a user triggers it with trigger_error(). + * + * @param int $severity + * @param string $message + * @param string|null $file + * @param int|null $line + * @param null $context + * + * @throws \ErrorException + */ + public function errorHandler(int $severity, string $message, string $file = null, int $line = null, $context = null) + { + // Convert it to an exception and pass it along. + throw new \ErrorException($message, 0, $severity, $file, $line); + } + + //-------------------------------------------------------------------- + + /** + * Checks to see if any errors have happened during shutdown that + * need to be caught and handle them. + */ + public function shutdownHandler() + { + $error = error_get_last(); + + // If we've got an error that hasn't been displayed, then convert + // it to an Exception and use the Exception handler to display it + // to the user. + if (! is_null($error)) + { + // Fatal Error? + if (in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE])) + { + $this->exceptionHandler(new \ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line'])); + } + } + } + + //-------------------------------------------------------------------- + + /** + * Determines the view to display based on the exception thrown, + * whether an HTTP or CLI request, etc. + * + * @param \Throwable $exception + * @param string $template_path + * + * @return string The path and filename of the view file to use + */ + protected function determineView(\Throwable $exception, string $template_path): string + { + // Production environments should have a custom exception file. + $view = 'production.php'; + $template_path = rtrim($template_path, '/ ').'/'; + + if (str_ireplace(['off', 'none', 'no', 'false', 'null'], '', ini_get('display_errors'))) + { + $view = 'error_exception.php'; + } + + // 404 Errors + if ($exception instanceof \CodeIgniter\PageNotFoundException) + { + return 'error_404.php'; + } + + // Allow for custom views based upon the status code + else if (is_file($template_path.'error_'.$exception->getCode().'.php')) + { + return 'error_'.$exception->getCode().'.php'; + } + + return $view; + } + + //-------------------------------------------------------------------- + + /** + * Determines the HTTP status code and the exit status code for this request. + * + * @param \Throwable $exception + * + * @return array + */ + protected function determineCodes(\Throwable $exception): array + { + $statusCode = abs($exception->getCode()); + + if ($statusCode < 100) + { + $exitStatus = $statusCode + EXIT__AUTO_MIN; // 9 is EXIT__AUTO_MIN + if ($exitStatus > EXIT__AUTO_MAX) // 125 is EXIT__AUTO_MAX + { + $exitStatus = EXIT_ERROR; // EXIT_ERROR + } + $statusCode = 500; + } + else + { + $exitStatus = 1; // EXIT_ERROR + } + + return [ + $statusCode ?? 500, + $exitStatus + ]; + } + + //-------------------------------------------------------------------- + + //-------------------------------------------------------------------- + // Display Methods + //-------------------------------------------------------------------- + + /** + * Clean Path + * + * This makes nicer looking paths for the error output. + * + * @param string $file + * + * @return string + */ + public static function cleanPath($file) + { + if (strpos($file, APPPATH) === 0) + { + $file = 'APPPATH/'.substr($file, strlen(APPPATH)); + } + elseif (strpos($file, BASEPATH) === 0) + { + $file = 'BASEPATH/'.substr($file, strlen(BASEPATH)); + } + elseif (strpos($file, FCPATH) === 0) + { + $file = 'FCPATH/'.substr($file, strlen(FCPATH)); + } + + return $file; + } + + //-------------------------------------------------------------------- + + /** + * Describes memory usage in real-world units. Intended for use + * with memory_get_usage, etc. + * + * @param $bytes + * + * @return string + */ + public static function describeMemory(int $bytes): string + { + if ($bytes < 1024) + { + return $bytes.'B'; + } + else if ($bytes < 1048576) + { + return round($bytes/1024, 2).'KB'; + } + + return round($bytes/1048576, 2).'MB'; + } + + //-------------------------------------------------------------------- + + + /** + * Creates a syntax-highlighted version of a PHP file. + * + * @param $file + * @param $lineNumber + * @param int $lines + * + * @return bool|string + */ + public static function highlightFile($file, $lineNumber, $lines = 15) + { + if (empty ($file) || ! is_readable($file)) + { + return false; + } + + // Set our highlight colors: + if (function_exists('ini_set')) + { + ini_set('highlight.comment', '#767a7e; font-style: italic'); + ini_set('highlight.default', '#c7c7c7'); + ini_set('highlight.html', '#06B'); + ini_set('highlight.keyword', '#f1ce61;'); + ini_set('highlight.string', '#869d6a'); + } + + try + { + $source = file_get_contents($file); + } + catch (\Throwable $e) + { + return false; + } + + $source = str_replace(["\r\n", "\r"], "\n", $source); + $source = explode("\n", highlight_string($source, true)); + $source = str_replace('
', "\n", $source[1]); + + $source = explode("\n", str_replace("\r\n", "\n", $source)); + + // Get just the part to show + $start = $lineNumber - (int)round($lines / 2); + $start = $start < 0 ? 0 : $start; + + // Get just the lines we need to display, while keeping line numbers... + $source = array_splice($source, $start, $lines, true); + + // Used to format the line number in the source + $format = '% '.strlen($start + $lines).'d'; + + $out = ''; + // Because the highlighting may have an uneven number + // of open and close span tags on one line, we need + // to ensure we can close them all to get the lines + // showing correctly. + $spans = 1; + + foreach ($source as $n => $row) + { + $spans += substr_count($row, ']+>#', $row, $tags); + $out .= sprintf("{$format} %s\n%s", + $n + $start + 1, + strip_tags($row), + implode('', $tags[0]) + ); + } + else + { + $out .= sprintf(''.$format.' %s', $n + $start +1, $row) ."\n"; + } + } + + $out .= str_repeat('', $spans); + + return '
'.$out.'
'; + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Debug/Iterator.php b/ci-4.0-dev/system/Debug/Iterator.php new file mode 100644 index 000000000..a5df8c402 --- /dev/null +++ b/ci-4.0-dev/system/Debug/Iterator.php @@ -0,0 +1,172 @@ +tests[$name] = $closure; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Runs through all of the tests that have been added, recording + * time to execute the desired number of iterations, and the approximate + * memory usage used during those iterations. + * + * @param int $iterations + * @param bool $output + * + * @return string + */ + public function run($iterations = 1000, $output=true) + { + foreach ($this->tests as $name => $test) + { + // clear memory before start + gc_collect_cycles(); + + $start = microtime(true); + $start_mem = $max_memory = memory_get_usage(true); + + for ($i = 0; $i < $iterations; $i++) + { + $result = call_user_func($test); + + $max_memory = max($max_memory, memory_get_usage(true)); + + unset($result); + } + + $this->results[$name] = [ + 'time' => microtime(true) - $start, + 'memory' => $max_memory - $start_mem, + 'n' => $iterations, + ]; + } + + if ($output) + { + return $this->getReport(); + } + } + + //-------------------------------------------------------------------- + + /** + * Get results. + * + * @return string + */ + public function getReport() + { + if (empty($this->results)) + { + return 'No results to display.'; + } + + // Template + $tpl = " + + + + + + + + + {rows} + +
TestTimeMemory
"; + + $rows = ""; + + foreach ($this->results as $name => $result) + { + $rows .= " + {$name} + ".number_format($result['time'], 4)." + {$result['memory']} + "; + } + + $tpl = str_replace('{rows}', $rows, $tpl); + + return $tpl ."
"; + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Debug/Timer.php b/ci-4.0-dev/system/Debug/Timer.php new file mode 100644 index 000000000..ce1c71549 --- /dev/null +++ b/ci-4.0-dev/system/Debug/Timer.php @@ -0,0 +1,179 @@ +timers[strtolower($name)] = [ + 'start' => ! empty($time) ? $time : microtime(true), + 'end' => null, + ]; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Stops a running timer. + * + * If the timer is not stopped before the timers() method is called, + * it will be automatically stopped at that point. + * + * @param string $name The name of this timer. + */ + public function stop(string $name) + { + $name = strtolower($name); + + if (empty($this->timers[$name])) + { + throw new \RuntimeException('Cannot stop timer: invalid name given.'); + } + + $this->timers[$name]['end'] = microtime(true); + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Returns the duration of a recorded timer. + * + * @param $name The name of the timer. + * @param int $decimals Number of decimal places. + * + * @return null|float Returns null if timer exists by that name. + * Returns a float representing the number of + * seconds elapsed while that timer was running. + */ + public function getElapsedTime(string $name, int $decimals = 4) + { + $name = strtolower($name); + + if (empty($this->timers[$name])) + { + return null; + } + + $timer = $this->timers[$name]; + + if (empty($timer['end'])) + { + $timer['end'] = microtime(true); + } + + return (float)number_format($timer['end'] - $timer['start'], $decimals); + } + + //-------------------------------------------------------------------- + + /** + * Returns the array of timers, with the duration pre-calculated for you. + * + * @param int $decimals Number of decimal places + * + * @return array + */ + public function getTimers(int $decimals = 4) + { + $timers = $this->timers; + + foreach ($timers as &$timer) + { + if (empty($timer['end'])) + { + $timer['end'] = microtime(true); + } + + $timer['duration'] = (float)number_format($timer['end'] - $timer['start'], $decimals); + } + + return $timers; + } + + //-------------------------------------------------------------------- + + /** + * Checks whether or not a timer with the specified name exists. + * + * @param string $name + * + * @return bool + */ + public function has(string $name) + { + return array_key_exists(strtolower($name), $this->timers); + } + + //-------------------------------------------------------------------- + + +} diff --git a/ci-4.0-dev/system/Debug/Toolbar.php b/ci-4.0-dev/system/Debug/Toolbar.php new file mode 100644 index 000000000..531560828 --- /dev/null +++ b/ci-4.0-dev/system/Debug/Toolbar.php @@ -0,0 +1,193 @@ +toolbarCollectors as $collector) + { + if ( ! class_exists($collector)) + { + // @todo Log this! + continue; + } + + $this->collectors[] = new $collector(); + } + } + + //-------------------------------------------------------------------- + + /** + * Run + * + * @param type $startTime + * @param type $totalTime + * @param type $startMemory + * @param type $request + * @param type $response + * @return type + */ + public function run($startTime, $totalTime, $startMemory, $request, $response): string + { + $this->startTime = $startTime; + + // Data items used within the view. + $collectors = $this->collectors; + + $totalTime = $totalTime * 1000; + $totalMemory = number_format((memory_get_peak_usage() - $startMemory) / 1048576, 3); + $segmentDuration = $this->roundTo($totalTime / 7, 5); + $segmentCount = (int)ceil($totalTime / $segmentDuration); + $varData = $this->collectVarData(); + + ob_start(); + include(__DIR__.'/Toolbar/View/toolbar.tpl.php'); + $output = ob_get_contents(); + ob_end_clean(); + + return $output; + } + + //-------------------------------------------------------------------- + + /** + * Called within the view to display the timeline itself. + * + * @param int $segmentCount + * @param int $segmentDuration + * @return string + */ + protected function renderTimeline(int $segmentCount, int $segmentDuration): string + { + $displayTime = $segmentCount * $segmentDuration; + + $rows = $this->collectTimelineData(); + + $output = ''; + + foreach ($rows as $row) + { + $output .= ""; + $output .= "{$row['name']}"; + $output .= "{$row['component']}"; + $output .= "".number_format($row['duration'] * 1000, 2)." ms"; + $output .= ""; + + $offset = ((($row['start'] - $this->startTime) * 1000) / + $displayTime) * 100; + $length = (($row['duration'] * 1000) / $displayTime) * 100; + + $output .= ""; + + $output .= ""; + + $output .= ""; + } + + return $output; + } + + //-------------------------------------------------------------------- + + /** + * Returns a sorted array of timeline data arrays from the collectors. + * + * @return array + */ + protected function collectTimelineData(): array + { + $data = []; + + // Collect it + foreach ($this->collectors as $collector) + { + if (! $collector->hasTimelineData()) + { + continue; + } + + $data = array_merge($data, $collector->timelineData()); + } + + // Sort it + + + return $data; + } + + //-------------------------------------------------------------------- + + /** + * Returns an array of data from all of the modules + * that should be displayed in the 'Vars' tab. + * + * @return array + */ + protected function collectVarData()// : array + { + $data = []; + + foreach ($this->collectors as $collector) + { + if (! $collector->hasVarData()) + { + continue; + } + + $data = array_merge($data, $collector->getVarData()); + } + + return $data; + } + + //-------------------------------------------------------------------- + + /** + * Rounds a number to the nearest incremental value. + * + * @param $number + * @param int $increments + * + * @return float + */ + protected function roundTo($number, $increments = 5) + { + $increments = 1 / $increments; + + return (ceil($number * $increments) / $increments); + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Debug/Toolbar/Collectors/BaseCollector.php b/ci-4.0-dev/system/Debug/Toolbar/Collectors/BaseCollector.php new file mode 100644 index 000000000..caf3b87c3 --- /dev/null +++ b/ci-4.0-dev/system/Debug/Toolbar/Collectors/BaseCollector.php @@ -0,0 +1,254 @@ +title)); + } + + return $this->title; + } + + //-------------------------------------------------------------------- + + /** + * Returns any information that should be shown next to the title. + * + * @return string + */ + public function getTitleDetails(): string + { + return ''; + } + + //-------------------------------------------------------------------- + + + + /** + * Does this collector need it's own tab? + * + * @return bool + */ + public function hasTabContent(): bool + { + return (bool)$this->hasTabContent; + } + + //-------------------------------------------------------------------- + + /** + * Does this collector have information for the timeline? + * + * @return bool + */ + public function hasTimelineData(): bool + { + return (bool)$this->hasTimeline; + } + + //-------------------------------------------------------------------- + + + /** + * Grabs the data for the timeline, properly formatted, + * or returns an empty array. + * + * @return bool + */ + public function timelineData(): array + { + if (! $this->hasTimeline) + { + return []; + } + + return $this->formatTimelineData(); + } + + //-------------------------------------------------------------------- + + /** + * Does this Collector have data that should be shown in the + * 'Vars' tab? + * + * @return mixed + */ + public function hasVarData() + { + return (bool)$this->hasVarData; + } + + //-------------------------------------------------------------------- + + /** + * Gets a collection of data that should be shown in the 'Vars' tab. + * The format is an array of sections, each with their own array + * of key/value pairs: + * + * $data = [ + * 'section 1' => [ + * 'foo' => 'bar, + * 'bar' => 'baz' + * ], + * 'section 2' => [ + * 'foo' => 'bar, + * 'bar' => 'baz' + * ], + * ]; + * + * @return null + */ + public function getVarData() + { + return null; + } + + //-------------------------------------------------------------------- + + + /** + * Child classes should implement this to return the timeline data + * formatted for correct usage. + * + * Timeline data should be formatted into arrays that look like: + * + * [ + * 'name' => 'Database::Query', + * 'component' => 'Database', + * 'start' => 10 // milliseconds + * 'duration' => 15 // milliseconds + * ] + * + * @return mixed + */ + protected function formatTimelineData(): array + { + return []; + } + + //-------------------------------------------------------------------- + + /** + * Builds and returns the HTML needed to fill a tab to display + * within the Debug Bar + * + * @return string + */ + public function display(): string + { + return ''; + } + + //-------------------------------------------------------------------- + + /** + * Clean Path + * + * This makes nicer looking paths for the error output. + * + * @param string $file + * + * @return string + */ + public function cleanPath($file) + { + if (strpos($file, APPPATH) === 0) + { + $file = 'APPPATH/'.substr($file, strlen(APPPATH)); + } + elseif (strpos($file, BASEPATH) === 0) + { + $file = 'BASEPATH/'.substr($file, strlen(BASEPATH)); + } + elseif (strpos($file, FCPATH) === 0) + { + $file = 'FCPATH/'.substr($file, strlen(FCPATH)); + } + + return $file; + } + +} diff --git a/ci-4.0-dev/system/Debug/Toolbar/Collectors/Database.php b/ci-4.0-dev/system/Debug/Toolbar/Collectors/Database.php new file mode 100644 index 000000000..74863cd78 --- /dev/null +++ b/ci-4.0-dev/system/Debug/Toolbar/Collectors/Database.php @@ -0,0 +1,213 @@ +connections = \Config\Database::getConnections(); + } + + //-------------------------------------------------------------------- + + /** + * The static method used during Hooks to collect + * data. + * + * @param \CodeIgniter\Database\Query $query + * + * @internal param $ array \CodeIgniter\Database\Query + */ + public static function collect(Query $query) + { + static::$queries[] = $query; + } + + //-------------------------------------------------------------------- + + /** + * Returns timeline data formatted for the toolbar. + * + * @return array The formatted data or an empty array. + */ + protected function formatTimelineData(): array + { + $data = []; + + foreach ($this->connections as $alias => $connection) + { + // Connection Time + $data[] = [ + 'name' => 'Connecting to Database: "'.$alias.'"', + 'component' => 'Database', + 'start' => $connection->getConnectStart(), + 'duration' => $connection->getConnectDuration() + ]; + } + + foreach (static::$queries as $query) + { + $data[] = [ + 'name' => 'Query', + 'component' => 'Database', + 'start' => $query->getStartTime(true), + 'duration' => $query->getDuration() + ]; + } + + return $data; + } + + //-------------------------------------------------------------------- + + /** + * Returns the HTML to fill the Database tab in the toolbar. + * + * @return string The data formatted for the toolbar. + */ + public function display(): string + { + // Key words we want bolded + $highlight = ['SELECT', 'DISTINCT', 'FROM', 'WHERE', 'AND', 'LEFT JOIN', 'ORDER BY', 'GROUP BY', + 'LIMIT', 'INSERT', 'INTO', 'VALUES', 'UPDATE', 'OR ', 'HAVING', 'OFFSET', 'NOT IN', + 'IN', 'LIKE', 'NOT LIKE', 'COUNT', 'MAX', 'MIN', 'ON', 'AS', 'AVG', 'SUM', '(', ')' + ]; + + $output = ''; + + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + + $output .= ''; + + foreach (static::$queries as $query) + { + $output .= ''; + $output .=''; + + $sql = $query->getQuery(); + + foreach ($highlight as $term) + { + $sql = str_replace($term, "{$term}", $sql); + } + + $output .= ''; + $output .= ''; + } + + $output .= ''; + + $output .= '
TimeQuery String
'.($query->getDuration(5) * 1000).' ms'.$sql.'
'; + + return $output; + } + + //-------------------------------------------------------------------- + + /** + * Information to be displayed next to the title. + * + * @return string The number of queries (in parentheses) or an empty string. + */ + public function getTitleDetails(): string + { + return '('.count(static::$queries).' Queries across '.count($this->connections).' Connection'. + (count($this->connections) > 1 ? 's' : '').')'; + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Debug/Toolbar/Collectors/Files.php b/ci-4.0-dev/system/Debug/Toolbar/Collectors/Files.php new file mode 100644 index 000000000..59bc97dcc --- /dev/null +++ b/ci-4.0-dev/system/Debug/Toolbar/Collectors/Files.php @@ -0,0 +1,122 @@ +"; + + $files = get_included_files(); + + $count = 0; + + foreach ($files as $file) + { + ++$count; + + $path = $this->cleanPath($file); + + if (strpos($path, 'BASEPATH') !== false) + { + $output .= ""; + } + else + { + $output .= ""; + } + + $output .= "". htmlspecialchars(str_replace('.php', '', basename($file)), ENT_SUBSTITUTE, 'UTF-8').""; + $output .= "".htmlspecialchars($path, ENT_SUBSTITUTE, 'UTF-8').""; + $output .= ""; + } + + $output .= ""; + + return $output; + } + + //-------------------------------------------------------------------- +} diff --git a/ci-4.0-dev/system/Debug/Toolbar/Collectors/Logs.php b/ci-4.0-dev/system/Debug/Toolbar/Collectors/Logs.php new file mode 100644 index 000000000..b480edae2 --- /dev/null +++ b/ci-4.0-dev/system/Debug/Toolbar/Collectors/Logs.php @@ -0,0 +1,104 @@ +logCache; + + if (empty($logs) || ! is_array($logs)) + { + return '

Nothing was logged. If you were expecting logged items, ensure that LoggerConfig file has the correct threshold set.

'; + } + + $output = ""; + + foreach ($logs as $log) + { + $output .= ""; + $output .= ""; + $output .= ""; + $output .= ""; + } + + return $output."
SeverityMessage
{$log['level']}".htmlspecialchars($log['msg'], ENT_SUBSTITUTE, 'UTF-8')."
"; + } + + //-------------------------------------------------------------------- + + +} diff --git a/ci-4.0-dev/system/Debug/Toolbar/Collectors/Routes.php b/ci-4.0-dev/system/Debug/Toolbar/Collectors/Routes.php new file mode 100644 index 000000000..04d8a48f0 --- /dev/null +++ b/ci-4.0-dev/system/Debug/Toolbar/Collectors/Routes.php @@ -0,0 +1,134 @@ +Matched Route"; + + $output .= ""; + + if ($match = $router->getMatchedRoute()) + { + $output .= ""; + $output .= ""; + } + + + $output .= ""; + $output .= ""; + $output .= ""; + + $method = new \ReflectionMethod($router->controllerName(), $router->methodName()); + $params = $method->getParameters(); + + $output .= ""; + + foreach($params as $key => $param) + { + $output .= ''; + } + + $output .= "
{$match[0]}{$match[1]}
Directory:".htmlspecialchars($router->directory())."
Controller:".htmlspecialchars($router->controllerName())."
Method:".htmlspecialchars($router->methodName())."
Params:".count($router->params())."/".count($params)."
'.$param->getName()." :"; + $output .= isset($router->params()[$key]) + ? $router->params()[$key] + : "<empty> | default: ".var_export($param->getDefaultValue(), true); + $output .= '
"; + + $output .= "

Defined Routes

"; + + $output .= ""; + + $routes = $routes->getRoutes(); + + foreach ($routes as $from => $to) + { + $output .= ""; + $output .= ""; + $output .= ""; + $output .= ""; + } + + $output .= "
".htmlspecialchars($from)."".htmlspecialchars($to)."
"; + + return $output; + } + + //-------------------------------------------------------------------- +} diff --git a/ci-4.0-dev/system/Debug/Toolbar/Collectors/Timers.php b/ci-4.0-dev/system/Debug/Toolbar/Collectors/Timers.php new file mode 100644 index 000000000..2c4e7fc55 --- /dev/null +++ b/ci-4.0-dev/system/Debug/Toolbar/Collectors/Timers.php @@ -0,0 +1,102 @@ +getTimers(6); + + foreach ($rows as $name => $info) + { + if ($name == 'total_execution') continue; + + $data[] = [ + 'name' => ucwords(str_replace('_', ' ', $name)), + 'component' => 'Timer', + 'start' => $info['start'], + 'duration' => $info['end'] - $info['start'] + ]; + } + + return $data; + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/Debug/Toolbar/Collectors/Views.php b/ci-4.0-dev/system/Debug/Toolbar/Collectors/Views.php new file mode 100644 index 000000000..d33da43db --- /dev/null +++ b/ci-4.0-dev/system/Debug/Toolbar/Collectors/Views.php @@ -0,0 +1,153 @@ +viewer = Services::renderer(null, true); + } + + //-------------------------------------------------------------------- + + + /** + * Child classes should implement this to return the timeline data + * formatted for correct usage. + * + * @return mixed + */ + protected function formatTimelineData(): array + { + $data = []; + + $rows = $this->viewer->getPerformanceData(); + + foreach ($rows as $name => $info) + { + $data[] = [ + 'name' => 'View: '.$info['view'], + 'component' => 'Views', + 'start' => $info['start'], + 'duration' => $info['end'] - $info['start'] + ]; + } + + return $data; + } + + //-------------------------------------------------------------------- + + /** + * Gets a collection of data that should be shown in the 'Vars' tab. + * The format is an array of sections, each with their own array + * of key/value pairs: + * + * $data = [ + * 'section 1' => [ + * 'foo' => 'bar, + * 'bar' => 'baz' + * ], + * 'section 2' => [ + * 'foo' => 'bar, + * 'bar' => 'baz' + * ], + * ]; + * + * @return null + */ + public function getVarData() + { + return [ + 'View Data' => $this->viewer->getData() + ]; + } + + //-------------------------------------------------------------------- + + +} diff --git a/ci-4.0-dev/system/Debug/Toolbar/View/toolbar.css b/ci-4.0-dev/system/Debug/Toolbar/View/toolbar.css new file mode 100644 index 000000000..e3012dfd8 --- /dev/null +++ b/ci-4.0-dev/system/Debug/Toolbar/View/toolbar.css @@ -0,0 +1,209 @@ +#debug-icon{ + position: fixed; + top: 0px; + left: 0px; + width: 30px; + height: 30px; + background: #fff; + border-bottom: 1px solid #ddd; + border-radius: 20%; + margin: 5px; + z-index: 10000; + box-shadow: 0 3px 10px rgba(0,0,0,0.2); + clear: both; +} + + #debug-icon a svg{ + margin: 1px ; + max-width:26px ; + max-height: 26px ; + } +#debug-bar a:active, #debug-bar a:link,#debug-bar a:visited { + color: #dd4814; +} +#debug-bar { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif ; + font-size: 14px ; + line-height: 1.5 ; + background: #fff ; + border-bottom: 1px solid #ddd ; + margin: 0; + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 10000; + box-shadow: 0 3px 10px rgba(0,0,0,0.1); + overflow: hidden; + overflow-y: auto; + max-height: 62% ; +} +#debug-bar h1, +#debug-bar h2, +#debug-bar h3{ + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif ; + color: #666 ; +} +#debug-bar p { + font-size: 12px ; + margin: 0 0 10px 20px ; + padding: 0 ; +} +#debug-bar a { + text-decoration: none; +} +#debug-bar a:hover { + text-decoration: underline; + text-decoration-color: #4e4a4a ; +} +#debug-bar .muted, +#debug-bar .muted td { + color: #bbb ; +} +#debug-bar .toolbar { + display: block ; + position: relative ; + overflow: hidden ; + overflow-y: auto ; + white-space: nowrap ; + border-bottom: 1px solid #eee; + padding: 3px 20px ; + text-align: right ; +} +#debug-bar h1 { + font-size: 16px ; + font-weight: 300 ; + margin: 0 2em 0 0 ; + padding: 0 ; + text-align: left ; + display: inline-block ; + float: left ; +} +#debug-bar h2 { + font-size: 16px ; + font-weight: 300 ; + margin: 0 ; + padding: 0 ; +} +#debug-bar h2 span { + font-size: 13px ; +} +#debug-bar h3 { + text-transform: uppercase ; + font-size: 11px ; + font-weight: 200 ; + margin-left: 10pt ; +} +#debug-bar span { + display: inline-block ; + font-size: 12px ; + padding: .3em 1em .3em ; + line-height: 1.0 ; + vertical-align: baseline ; +} +#debug-bar table strong { + font-weight: 500 ; + color: rgba(0,0,0,0.3) ; +} +#debug-bar .ci-label { + border-radius: 0.25em ; + text-shadow: none ; + background-color: #eee ; + border: 1px solid #ddd ; + margin-left: 0.4em ; +} +#debug-bar .ci-label a { + display: block ; + width: 100% ; + height: 100% ; + color: inherit ; + text-decoration: none ; +} +#debug-bar .ci-label a:hover { + text-decoration: underline ; +} +#debug-bar .ci-label.active { + font-weight: 400 ; + background-color: #ccc ; + border-color: #bbb ; + padding: .3em 0.9em .3em ; +} +#debug-bar .tab { + display: none; + background: inherit ; + padding: 1em 2em; +} + +#debug-bar table { + margin: 0 0 10px 20px ; + font-size: 13px ; + border-collapse:collapse ; + width: 100% ; +} +#debug-bar td, +#debug-bar th { + display: table-cell ; + text-align: left ; +} +#debug-bar tr { + border: none ; +} +#debug-bar td { + border: none ; + padding: 2px 10px 2px 5px; + margin: 0 ; + +} +#debug-bar th { + padding-bottom: 0.7em ; +} +#debug-bar tr td:first-child { + width: 20% ; +} +#debug-bar tr td:first-child.narrow { + width: 7em ; +} +#debug-bar tr:hover { + background-color: #f3f3f3 ; +} +#debug-bar table.timeline { + width: 100% ; + margin-left: 0 ; +} +#debug-bar table.timeline th { + font-size: 0.7em ; + font-weight: 200 ; + text-align: left ; + padding-bottom: 1em ; +} +#debug-bar table.timeline td, +#debug-bar table.timeline th { + border-left: 1px solid #ddd ; + padding: 0 1em; + position: relative; +} +#debug-bar table.timeline tr td:first-child, +#debug-bar table.timeline tr th:first-child { + border-left: 0 ; + padding-left: 0 ; +} +#debug-bar table.timeline td { + padding: 5px; +} +#debug-bar table.timeline .timer { + position: absolute ; + display: inline-block ; + padding: 3px ; + bottom: 9px ; + border-radius: 3px ; + background-color: #999 ; +} +#debug-bar .route-params, +#debug-bar .route-params-item { + vertical-align: top ; +} +#debug-bar .route-params-item td:first-child { + padding-left: 1em ; + text-align: right ; + font-style: italic ; +} diff --git a/ci-4.0-dev/system/Debug/Toolbar/View/toolbar.js b/ci-4.0-dev/system/Debug/Toolbar/View/toolbar.js new file mode 100644 index 000000000..7d31d9daa --- /dev/null +++ b/ci-4.0-dev/system/Debug/Toolbar/View/toolbar.js @@ -0,0 +1,198 @@ +/* + * Functionality for the CodeIgniter Debug Toolbar. + */ + +var ciDebugBar = { + + toolbar : null, + + //-------------------------------------------------------------------- + + init : function() + { + this.toolbar = document.getElementById('debug-bar'); + + ciDebugBar.createListeners(); + ciDebugBar.setToolbarState(); + }, + + //-------------------------------------------------------------------- + + createListeners : function() + { + var buttons = [].slice.call(document.querySelectorAll('#debug-bar .ci-label a')); + + for (var i=0; i < buttons.length; i++) + { + buttons[i].addEventListener('click', ciDebugBar.showTab, true); + } + }, + + //-------------------------------------------------------------------- + + showTab: function() + { + // Get the target tab, if any + var tab = this.getAttribute('data-tab'); + + // Check our current state. + var state = document.getElementById(tab).style.display; + + if (tab == undefined) return true; + + // Hide all tabs + var tabs = document.querySelectorAll('#debug-bar .tab'); + + for (var i=0; i < tabs.length; i++) + { + tabs[i].style.display = 'none'; + } + + // Mark all labels as inactive + var labels = document.querySelectorAll('#debug-bar .ci-label'); + + for (var i=0; i < labels.length; i++) + { + ciDebugBar.removeClass(labels[i], 'active'); + } + + // Show/hide the selected tab + if (state != 'block') + { + document.getElementById(tab).style.display = 'block'; + ciDebugBar.addClass(this.parentNode, 'active'); + } + }, + + //-------------------------------------------------------------------- + + addClass : function(el, className) + { + if (el.classList) + { + el.classList.add(className); + } + else + { + el.className += ' ' + className; + } + }, + + //-------------------------------------------------------------------- + + removeClass : function(el, className) + { + if (el.classList) + { + el.classList.remove(className); + } + else + { + el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); + } + + }, + + //-------------------------------------------------------------------- + + /** + * Toggle display of a data table + * @param obj + */ + toggleDataTable : function(obj) + { + if (typeof obj == 'string') + { + obj = document.getElementById(obj + '_table'); + } + + if (obj) + { + obj.style.display = obj.style.display == 'none' ? 'block' : 'none'; + } + }, + + //-------------------------------------------------------------------- + + /** + * Toggle tool bar from full to icon and icon to full + */ + toggleToolbar : function() + { + var ciIcon = document.getElementById('debug-icon'); + var ciBar = document.getElementById('debug-bar'); + var open = ciBar.style.display != 'none'; + + ciIcon.style.display = open == true ? 'inline-block' : 'none'; + ciBar.style.display = open == false ? 'inline-block' : 'none'; + + // Remember it for other page loads on this site + ciDebugBar.createCookie('debug-bar-state', '', -1); + ciDebugBar.createCookie('debug-bar-state', open == true ? 'minimized' : 'open' , 365); + }, + + //-------------------------------------------------------------------- + + /** + * Sets the initial state of the toolbar (open or minimized) when + * the page is first loaded to allow it to remember the state between refreshes. + */ + setToolbarState: function() + { + var open = ciDebugBar.readCookie('debug-bar-state'); + var ciIcon = document.getElementById('debug-icon'); + var ciBar = document.getElementById('debug-bar'); + + ciIcon.style.display = open != 'open' ? 'inline-block' : 'none'; + ciBar.style.display = open == 'open' ? 'inline-block' : 'none'; + }, + + //-------------------------------------------------------------------- + + /** + * Helper to create a cookie. + * + * @param name + * @param value + * @param days + */ + createCookie : function(name,value,days) + { + if (days) + { + var date = new Date(); + + date.setTime(date.getTime()+(days*24*60*60*1000)); + + var expires = "; expires="+date.toGMTString(); + } + else + { + var expires = ""; + } + + document.cookie = name+"="+value+expires+"; path=/"; + }, + + //-------------------------------------------------------------------- + + readCookie : function(name) + { + var nameEQ = name + "="; + var ca = document.cookie.split(';'); + + for(var i=0;i < ca.length;i++) + { + var c = ca[i]; + while (c.charAt(0)==' ') + { + c = c.substring(1,c.length); + } + if (c.indexOf(nameEQ) == 0) + { + return c.substring(nameEQ.length,c.length); + } + } + return null; + } +}; diff --git a/ci-4.0-dev/system/Debug/Toolbar/View/toolbar.tpl.php b/ci-4.0-dev/system/Debug/Toolbar/View/toolbar.tpl.php new file mode 100644 index 000000000..e7ef4e1c0 --- /dev/null +++ b/ci-4.0-dev/system/Debug/Toolbar/View/toolbar.tpl.php @@ -0,0 +1,245 @@ + + + + +
+
+

Debug Bar

+ + ms + MB + Timeline + collectors as $c) : ?> + hasTabContent()) : ?> + getTitle()) ?> + + + Vars +
+ + +
+ + + + + + + + + + + + + renderTimeline($segmentCount, $segmentDuration, $totalTime) ?> + +
NAMECOMPONENTDURATION ms
+
+ + + collectors as $c) : ?> + hasTabContent()) : ?> +
+

getTitle()) ?> getTitleDetails()) ?>

+ + display() ?> +
+ + + + +
+ + + $items) : ?> + + +

+
+ + + + + + $value) : ?> + + + + + + +
+ +
+ + +

No data to display.

+ + + + + +

Session User Data

+
+ + + + + + $value) : ?> + + + + + + +
+ +
+ +

No data to display.

+ + +

Session doesn't seem to be active.

+ + +

Request ( isSecure() ? 'HTTPS' : 'HTTP').'/'.$request->getProtocolVersion() ?> )

+ + getGet()) : ?> + +

$_GET

+
+ + + + $value) : ?> + + + + + + +
+ + + getPost()) : ?> + +

$_POST

+
+ + + + $value) : ?> + + + + + + +
+ + + getHeaders()) : ?> + +

Headers

+
+ + + + + $value) : ?> + + + + + + + + + + +
getName()) ?>getValueLine()) ?>
+ + + getCookie()) : ?> + +

Cookies

+
+ + + + $value) : ?> + + + + + + + + + +

Response ( getStatusCode().' - '. esc($response->getReason()) ?> )

+ + getHeaders()) : ?> + +

Headers

+
+ + + + $value) : ?> + + + + + + +
getHeaderLine($header)) ?>
+ +
+ + + diff --git a/ci-4.0-dev/system/Filters/FilterInterface.php b/ci-4.0-dev/system/Filters/FilterInterface.php new file mode 100644 index 000000000..db77cd0c0 --- /dev/null +++ b/ci-4.0-dev/system/Filters/FilterInterface.php @@ -0,0 +1,40 @@ + [], + 'after' => [] + ]; + + /** + * The original config file + * @var BaseConfig + */ + protected $config; + + /** + * The active IncomingRequest or CLIRequest + * @var RequestInterface + */ + protected $request; + + /** + * The active Response instance + * @var ResponseInterface + */ + protected $response; + + /** + * Whether we've done initial processing + * on the filter lists. + * @var bool + */ + protected $initialized = false; + + //-------------------------------------------------------------------- + + public function __construct($config, RequestInterface $request, ResponseInterface $response) + { + $this->config = $config; + $this->request =& $request; + $this->response =& $response; + } + + //-------------------------------------------------------------------- + + /** + * Runs through all of the filters for the specified + * uri and position. + * + * @param string $uri + * @param string $position + * + * @return \CodeIgniter\HTTP\RequestInterface|\CodeIgniter\HTTP\ResponseInterface|mixed + */ + public function run(string $uri, $position = 'before') + { + $this->initialize($uri); + + foreach ($this->filters[$position] as $alias => $rules) + { + if (is_numeric($alias) && is_string($rules)) + { + $alias = $rules; + } + + if (! array_key_exists($alias, $this->config->aliases)) + { + throw new \InvalidArgumentException("'{$alias}' filter must have a matching alias defined."); + } + + $class = new $this->config->aliases[$alias](); + + if (! $class instanceof FilterInterface) + { + throw new \RuntimeException(get_class($class). ' must implement CodeIgniter\Filters\FilterInterface.'); + } + + if ($position == 'before') + { + $result = $class->before($this->request); + + if ($result instanceof RequestInterface) + { + $this->request = $result; + continue; + } + + // If the response object was sent back, + // then send it and quit. + if ($result instanceof ResponseInterface) + { + $result->send(); + exit(EXIT_ERROR); + } + + if (empty($result)) + { + continue; + } + + return $result; + } + + elseif ($position == 'after') + { + $result = $class->after($this->request, $this->response); + + if ($result instanceof ResponseInterface) + { + $this->response = $result; + continue; + } + } + } + + return $position == 'before' + ? $this->request + : $this->response; + } + + //-------------------------------------------------------------------- + + /** + * Runs through our list of filters provided by the configuration + * object to get them ready for use, including getting uri masks + * to proper regex, removing those we can from the possibilities + * based on HTTP method, etc. + * + * The resulting $this->filters is an array of only filters + * that should be applied to this request. + * + * We go ahead an process the entire tree because we'll need to + * run through both a before and after and don't want to double + * process the rows. + */ + public function initialize(string $uri = null) + { + if ($this->initialized === true) + { + return; + } + + $this->processGlobals($uri); + $this->processMethods(); + $this->processFilters($uri); + + $this->initialized = true; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Returns the processed filters array. + * + * @return array + */ + public function getFilters() + { + return $this->filters; + } + + //-------------------------------------------------------------------- + + //-------------------------------------------------------------------- + // Processors + //-------------------------------------------------------------------- + + protected function processGlobals(string $uri = null) + { + if (! isset($this->config->globals) || ! is_array($this->config->globals)) + { + return; + } + + // Before + if (isset($this->config->globals['before'])) + { + // Take any 'except' routes into consideration + foreach ($this->config->globals['before'] as $alias => $rules) + { + if (! is_array($rules) || ! array_key_exists('except', $rules)) + { + continue; + } + + $rules = $rules['except']; + + foreach ($rules as $path) + { + // Prep it for regex + $path = str_replace('/*', '*', $path); + $path = trim(str_replace('*', '.+', $path), '/ '); + + // Path doesn't match the URI? continue on... + if (preg_match('/'.$path.'/', $uri, $match) !== 1) + { + continue; + } + + unset($this->config->globals['before'][$alias]); + break; + } + } + + $this->filters['before'] = array_merge($this->filters['before'], $this->config->globals['before']); + } + + // After + if (isset($this->config->globals['after'])) + { + // Take any 'except' routes into consideration + foreach ($this->config->globals['after'] as $alias => $rules) + { + if (! is_array($rules) || ! array_key_exists('except', $rules)) + { + continue; + } + + $rules = $rules['except']; + + if (is_string($rules)) + { + $rules = [$rules]; + } + + foreach ($rules as $path) + { + // Prep it for regex + $path = str_replace('/*', '*', $path); + $path = trim(str_replace('*', '.+', $path), '/ '); + + // Path doesn't match the URI? continue on... + if (preg_match('/'.$path.'/', $uri, $match) !== 1) + { + continue; + } + + unset($this->config->globals['after'][$alias]); + break; + } + } + + $this->filters['after'] = array_merge($this->filters['after'], $this->config->globals['after']); + } + } + + //-------------------------------------------------------------------- + + protected function processMethods() + { + if (! isset($this->config->methods) || ! is_array($this->config->methods)) + { + return; + } + + // Request method won't be set for CLI-based requests + $method = isset($_SERVER['REQUEST_METHOD']) + ? strtolower($_SERVER['REQUEST_METHOD']) + : 'cli'; + + if (array_key_exists($method, $this->config->methods)) + { + $this->filters['before'] = array_merge($this->filters['before'], $this->config->methods[$method]); + return; + } + } + + //-------------------------------------------------------------------- + + protected function processFilters(string $uri = null) + { + if (! isset($this->config->filters) || ! count($this->config->filters)) + { + return; + } + + $uri = trim($uri, '/ '); + + $matches = []; + + foreach ($this->config->filters as $alias => $settings) + { + // Before + if (isset($settings['before'])) + { + foreach ($settings['before'] as $path) + { + // Prep it for regex + $path = str_replace('/*', '*', $path); + $path = trim(str_replace('*', '.+', $path), '/ '); + + if (preg_match('/'.$path.'/', $uri) !== 1) + { + continue; + } + + $matches[] = $alias; + } + + $this->filters['before'] = array_merge($this->filters['before'], $matches); + $matches = []; + } + + // After + if (isset($settings['after'])) + { + foreach ($settings['after'] as $path) + { + // Prep it for regex + $path = str_replace('/*', '*', $path); + $path = trim(str_replace('*', '.+', $path), '/ '); + + if (preg_match('/'.$path.'/', $uri) !== 1) + { + continue; + } + + $matches[] = $alias; + } + + $this->filters['after'] = array_merge($this->filters['after'], $matches); + } + } + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/HTTP/CLIRequest.php b/ci-4.0-dev/system/HTTP/CLIRequest.php new file mode 100644 index 000000000..1d3cd572b --- /dev/null +++ b/ci-4.0-dev/system/HTTP/CLIRequest.php @@ -0,0 +1,238 @@ +parseCommand(); + } + + //-------------------------------------------------------------------- + + /** + * Returns the "path" of the request script so that it can be used + * in routing to the appropriate controller/method. + * + * The path is determined by treating the command line arguments + * as if it were a URL - up until we hit our first option. + * + * Example: + * php index.php users 21 profile -foo bar + * + * // Routes to /users/21/profile (index is removed for routing sake) + * // with the option foo = bar. + * + * @return string + */ + public function getPath(): string + { + $path = implode('/', $this->segments); + + return empty($path) ? '' : $path; + } + + //-------------------------------------------------------------------- + + /** + * Returns an associative array of all CLI options found, with + * their values. + * + * @return array + */ + public function getOptions(): array + { + return $this->options; + } + + //-------------------------------------------------------------------- + + /** + * Returns the value for a single CLI option that was passed in. + * + * @param string $key + * + * @return string|null + */ + public function getOption(string $key) + { + if (array_key_exists($key, $this->options)) + { + return $this->options[$key]; + } + + return null; + } + + //-------------------------------------------------------------------- + + /** + * Returns the options as a string, suitable for passing along on + * the CLI to other commands. + * + * Example: + * $options = [ + * 'foo' => 'bar', + * 'baz' => 'queue some stuff' + * ]; + * + * getOptionString() = '-foo bar -baz "queue some stuff"' + * + * @return string + */ + public function getOptionString(): string + { + if (empty($this->options)) + { + return ''; + } + + $out = ''; + + foreach ($this->options as $name => $value) + { + // If there's a space, we need to group + // so it will pass correctly. + if (strpos($value, ' ') !== false) + { + $value = '"'. $value .'"'; + } + + $out .= "-{$name} $value "; + } + + return $out; + } + + //-------------------------------------------------------------------- + + /** + * Parses the command line it was called from and collects all options + * and valid segments. + * + * NOTE: I tried to use getopt but had it fail occasionally to find + * any options, where argv has always had our back. + */ + protected function parseCommand() + { + // Since we're building the options ourselves, + // we stop adding it to the segments array once + // we have found the first dash. + $options_found = false; + + $argc = $this->getServer('argc', FILTER_SANITIZE_NUMBER_INT); + $argv = $this->getServer('argv'); + + // We start at 1 since we never want to include index.php + for ($i = 1; $i < $argc; $i++) + { + // If there's no '-' at the beginning of the argument + // then add it to our segments. + if ( ! $options_found && strpos($argv[$i], '-') === false) + { + $this->segments[] = filter_var($argv[$i], FILTER_SANITIZE_STRING); + continue; + } + + $options_found = true; + + if (substr($argv[$i], 0, 1) != '-') + { + continue; + } + + $arg = filter_var(str_replace('-', '', $argv[$i]), FILTER_SANITIZE_STRING); + $value = null; + + // If the next item starts with a dash it's a value + if (isset($argv[$i + 1]) && substr($argv[$i + 1], 0, 1) != '-' ) + { + $value = filter_var($argv[$i + 1], FILTER_SANITIZE_STRING); + $i++; + } + + $this->options[$arg] = $value; + } + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/HTTP/CURLRequest.php b/ci-4.0-dev/system/HTTP/CURLRequest.php new file mode 100644 index 000000000..e13c5a1da --- /dev/null +++ b/ci-4.0-dev/system/HTTP/CURLRequest.php @@ -0,0 +1,733 @@ + 0.0, + 'connect_timeout' => 150, + 'debug' => false, + 'verify' => true + ]; + + /** + * Default values for when 'allow_redirects' + * option is true. + * + * @var array + */ + protected $redirectDefaults = [ + 'max' => 5, + 'strict' => true, + 'protocols' => ['http', 'https'], + ]; + + /** + * The number of milliseconds to delay before + * sending the request. + * @var float + */ + protected $delay = 0.0; + + //-------------------------------------------------------------------- + + /** + * Takes an array of options to set the following possible class properties: + * + * - baseURI + * - timeout + * - any other request options to use as defaults. + * + * @param App $config + * @param URI $uri + * @param ResponseInterface $response + * @param array $options + */ + public function __construct(App $config, URI $uri, ResponseInterface $response = null, array $options = []) + { + if ( ! function_exists('curl_version')) + { + throw new \RuntimeException('CURL must be enabled to use the CURLRequest class.'); + } + + parent::__construct($config); + + $this->response = $response; + $this->baseURI = $uri; + + $this->parseOptions($options); + } + + //-------------------------------------------------------------------- + + /** + * Sends an HTTP request to the specified $url. If this is a relative + * URL, it will be merged with $this->baseURI to form a complete URL. + * + * @param $method + * @param string $url + * @param array $options + * + * @return Response + */ + public function request($method, string $url, array $options = []): ResponseInterface + { + $this->parseOptions($options); + + $url = $this->prepareURL($url); + + $method = filter_var($method, FILTER_SANITIZE_STRING); + + $this->send($method, $url); + + return $this->response; + } + + //-------------------------------------------------------------------- + + /** + * Convenience method for sending a GET request. + * + * @param string $url + * @param array $options + * + * @return Response + */ + public function get(string $url, array $options = []): ResponseInterface + { + return $this->request('get', $url, $options); + } + + //-------------------------------------------------------------------- + + /** + * Convenience method for sending a DELETE request. + * + * @param string $url + * @param array $options + * + * @return Response + */ + public function delete(string $url, array $options = []): ResponseInterface + { + return $this->request('delete', $url, $options); + } + + //-------------------------------------------------------------------- + + /** + * Convenience method for sending a HEAD request. + * + * @param string $url + * @param array $options + * + * @return Response + */ + public function head(string $url, array $options = []): ResponseInterface + { + return $this->request('head', $url, $options); + } + + //-------------------------------------------------------------------- + + /** + * Convenience method for sending an OPTIONS request. + * + * @param string $url + * @param array $options + * + * @return Response + */ + public function options(string $url, array $options = []): ResponseInterface + { + return $this->request('options', $url, $options); + } + + //-------------------------------------------------------------------- + + /** + * Convenience method for sending a PATCH request. + * + * @param string $url + * @param array $options + * + * @return Response + */ + public function patch(string $url, array $options = []): ResponseInterface + { + return $this->request('patch', $url, $options); + } + + //-------------------------------------------------------------------- + + /** + * Convenience method for sending a POST request. + * + * @param string $url + * @param array $options + * + * @return Response + */ + public function post(string $url, array $options = []): ResponseInterface + { + return $this->request('post', $url, $options); + } + + //-------------------------------------------------------------------- + + /** + * Convenience method for sending a PUT request. + * + * @param string $url + * @param array $options + * + * @return Response + */ + public function put(string $url, array $options = []): ResponseInterface + { + return $this->request('put', $url, $options); + } + + //-------------------------------------------------------------------- + + /** + * Sets the correct settings based on the options array + * passed in. + * + * @param array $options + */ + protected function parseOptions(array $options) + { + if (array_key_exists('baseURI', $options)) + { + $this->baseURI = $this->baseURI->setURI($options['baseURI']); + unset($options['baseURI']); + } + + if (array_key_exists('headers', $options) && is_array($options['headers'])) + { + foreach ($options['headers'] as $name => $value) + { + $this->setHeader($name, $value); + } + + unset($options['headers']); + } + + if (array_key_exists('delay', $options)) + { + // Convert from the milliseconds passed in + // to the seconds that sleep requires. + $this->delay = (float)$options['delay'] / 1000; + unset($options['delay']); + } + + foreach ($options as $key => $value) + { + $this->config[$key] = $value; + } + } + + //-------------------------------------------------------------------- + + /** + * If the $url is a relative URL, will attempt to create + * a full URL by prepending $this->baseURI to it. + * + * @param string $url + * + * @return string + */ + protected function prepareURL(string $url): string + { + // If it's a full URI, then we have nothing to do here... + if (strpos($url, '://') !== false) + { + return $url; + } + + $uri = $this->baseURI->resolveRelativeURI($url); + + return (string)$uri; + } + + //-------------------------------------------------------------------- + + /** + * Get the request method. Overrides the Request class' method + * since users expect a different answer here. + * + * @param bool|false $upper Whether to return in upper or lower case. + * + * @return string + */ + public function getMethod($upper = false): string + { + return ($upper) + ? strtoupper($this->method) + : strtolower($this->method); + } + + //-------------------------------------------------------------------- + + /** + * Fires the actual cURL request. + * + * @param string $method + * @param string $url + */ + public function send(string $method, string $url) + { + // Reset our curl options so we're on a fresh slate. + $curl_options = []; + + if (! empty($this->config['query']) && is_array($this->config['query'])) + { + // This is likely too naive a solution. + // Should look into handling when $url already + // has query vars on it. + $url .= '?'. http_build_query($this->config['query']); + unset($this->config['query']); + } + + $curl_options[CURLOPT_URL] = $url; + $curl_options[CURLOPT_RETURNTRANSFER] = true; + $curl_options[CURLOPT_HEADER] = true; + $curl_options[CURLOPT_FRESH_CONNECT] = true; + // Disable @file uploads in post data. + $curl_options[CURLOPT_SAFE_UPLOAD] = true; + + $curl_options = $this->setCURLOptions($curl_options, $this->config); + $curl_options = $this->applyMethod($method, $curl_options); + $curl_options = $this->applyRequestHeaders($curl_options); + + // Do we need to delay this request? + if ($this->delay > 0) + { + sleep($this->delay); + } + + $output = $this->sendRequest($curl_options); + + // Split out our headers and body + $break = strpos($output, "\r\n\r\n"); + + if ($break !== false) + { + // Our headers + $headers = explode("\n", substr($output, 0, $break)); + + $this->setResponseHeaders($headers); + + // Our body + $body = substr($output, $break + 4); + $this->response->setBody($body); + } + else + { + $this->response->setBody($output); + } + + return $this->response; + } + + //-------------------------------------------------------------------- + + /** + * Takes all headers current part of this request and adds them + * to the cURL request. + * + * @param array $curl_options + */ + protected function applyRequestHeaders(array $curl_options = []): array + { + $headers = $this->getHeaders(); + + if (empty($head)) + { + return $curl_options; + } + + $set = []; + + foreach ($headers as $name => $value) + { + $set[] = $name.': '.$this->getHeaderLine($name); + } + + $curl_options[CURLOPT_HTTPHEADER] = $set; + + return $curl_options; + } + + //-------------------------------------------------------------------- + + /** + * Apply method + * + * @param type $method + * @param array $curl_options + * @return int + */ + protected function applyMethod($method, array $curl_options): array + { + $method = strtoupper($method); + + $this->method = $method; + $curl_options[CURLOPT_CUSTOMREQUEST] = $method; + + $size = strlen($this->body); + + // Have content? + if ($size === null || $size > 0) + { + $curl_options = $this->applyBody($curl_options); + + return $curl_options; + } + + if ($method == 'PUT' || $method == 'POST') + { + // See http://tools.ietf.org/html/rfc7230#section-3.3.2 + if (is_null($this->getHeader('content-length'))) + { + $this->setHeader('Content-Length', 0); + } + } + else if ($method == 'HEAD') + { + $curl_options[CURLOPT_NOBODY] = 1; + } + + return $curl_options; + } + + //-------------------------------------------------------------------- + + /** + * Apply body + * + * @param array $curl_options + * @return type + */ + protected function applyBody(array $curl_options = []): array + { + if ( ! empty($this->body)) + { + $curl_options[CURLOPT_POSTFIELDS] = (string)$this->getBody(); + } + + return $curl_options; + } + + //-------------------------------------------------------------------- + + /** + * Parses the header retrieved from the cURL response into + * our Response object. + * + * @param array $headers + */ + protected function setResponseHeaders(array $headers = []) + { + foreach ($headers as $header) + { + if (($pos = strpos($header, ':')) !== false) + { + $title = substr($header, 0, $pos); + $value = substr($header, $pos + 1); + + $this->response->setHeader($title, $value); + } + else if (substr($header, 0, 4) == 'HTTP') + { + preg_match('#^HTTP\/([12]\.[01]) ([0-9]+) (.+)#', $header, $matches); + + if (isset($matches[1])) + { + $this->response->setProtocolVersion($matches[1]); + } + + if (isset($matches[2])) + { + $this->response->setStatusCode($matches[2], isset($matches[3]) ? $matches[3] : null); + } + } + } + } + + //-------------------------------------------------------------------- + + /** + * Set CURL options + * + * @param array $curl_options + * @param array $config + * @return type + * @throws \InvalidArgumentException + */ + protected function setCURLOptions(array $curl_options = [], array $config = []) + { + // Auth Headers + if ( ! empty($config['auth'])) + { + $curl_options[CURLOPT_USERPWD] = $config['auth'][0].':'.$config['auth'][1]; + + if ( ! empty($config['auth'][2]) && strtolower($config['auth'][2]) == 'digest') + { + $curl_options[CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST; + } + else + { + $curl_options[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC; + } + } + + // Certificate + if ( ! empty($config['cert'])) + { + $cert = $config['cert']; + + if (is_array($cert)) + { + $curl_options[CURLOPT_SSLCERTPASSWD] = $cert[1]; + $cert = $cert[0]; + } + + if ( ! file_exists($cert)) + { + throw new \InvalidArgumentException('SSL certificate not found at: '.$cert); + } + + $curl_options[CURLOPT_SSLCERT] = $cert; + } + + // SSL Verification + if (isset($config['verify'])) + { + if (is_string($config['verify'])) + { + $file = realpath($config['ssl_key']); + + if ( ! $file) + { + throw new \InvalidArgumentException('Cannot set SSL Key. '.$config['ssl_key']. + ' is not a valid file.'); + } + $curl_options[CURLOPT_CAINFO] = $file; + $curl_options[CURLOPT_SSL_VERIFYPEER] = 1; + } + else if (is_bool($config['verify'])) + { + $curl_options[CURLOPT_SSL_VERIFYPEER] = $config['verify']; + } + } + + // Debug + if (isset($config['debug'])) + { + $curl_options[CURLOPT_VERBOSE] = 1; + $curl_options[CURLOPT_STDERR] = is_bool($config['debug']) ? fopen('php://output', 'w+') : $config['debug']; + } + + // Decode Content + if ( ! empty($config['decode_content'])) + { + $accept = $this->getHeaderLine('Accept-Encoding'); + + if ($accept) + { + $curl_options[CURLOPT_ENCODING] = $accept; + } + else + { + $curl_options[CURLOPT_ENCODING] = ''; + $curl_options[CURLOPT_HTTPHEADER] = 'Accept-Encoding'; + } + } + + // Allow Redirects + if (array_key_exists('allow_redirects', $config)) + { + $settings = $this->redirectDefaults; + + if (is_array($config['allow_redirects'])) + { + $settings = array_merge($settings, $config['allow_redirects']); + } + + if ($config['allow_redirects'] === false) + { + $curl_options[CURLOPT_FOLLOWLOCATION] = 0; + } + else + { + $curl_options[CURLOPT_FOLLOWLOCATION] = 1; + $curl_options[CURLOPT_MAXREDIRS] = $settings['max']; + + if ($settings['strict'] === true) + { + $curl_options[CURLOPT_POSTREDIR] = 1|2|4; + } + + $protocols = 0; + foreach ($settings['protocols'] as $proto) + { + $protocols += constant('CURLPROTO_'.strtoupper($proto)); + } + + $curl_options[CURLOPT_REDIR_PROTOCOLS] = $protocols; + } + } + + // Timeout + $curl_options[CURLOPT_TIMEOUT_MS] = (float)$config['timeout'] * 1000; + + // Connection Timeout + $curl_options[CURLOPT_CONNECTTIMEOUT_MS] = (float)$config['connect_timeout'] * 1000; + + // Post Data - application/x-www-form-urlencoded + if (! empty($config['form_params']) && is_array($config['form_params'])) + { + $curl_options[CURLOPT_POSTFIELDS] = http_build_query($config['form_params']); + + if (empty($this->header('Content-Type'))) + { + $this->setHeader('Content-Type', 'application/x-www-form-urlencoded'); + } + } + + // Post Data - multipart/form-data + if (! empty($config['multipart']) && is_array($config['multipart'])) + { + // setting the POSTFIELDS option automatically sets multipart + $curl_options[CURLOPT_POSTFIELDS] = $config['multipart']; + } + + // HTTP Errors + $curl_options[CURLOPT_FAILONERROR] = array_key_exists('http_errors', $config) + ? (bool)$config['http_errors'] + : true; + + // JSON + if (isset($config['json'])) + { + // Will be set as the body in `applyBody()` + $this->setBody(json_encode($config['json'])); + $this->setHeader('Content-Type', 'application/json'); + } + + // version + if (! empty($config['version'])) + { + if ($config['version'] == 1.0) + { + $curl_options[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0; + } + else if ($config['version'] == 1.1) + { + $curl_options[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1; + } + } + + return $curl_options; + } + + //-------------------------------------------------------------------- + + /** + * Does the actual work of initializing cURL, setting the options, + * and grabbing the output. + * + * @param array $curl_options + * + * @return string + */ + protected function sendRequest(array $curl_options = []): string + { + $ch = curl_init(); + + curl_setopt_array($ch, $curl_options); + + // Send the request and wait for a response. + $output = curl_exec($ch); + + if ($output === false) + { + throw new \RuntimeException(curl_errno($ch).': '.curl_error($ch)); + } + + curl_close($ch); + + return $output; + } + + //-------------------------------------------------------------------- + +} diff --git a/ci-4.0-dev/system/HTTP/ContentSecurityPolicy.php b/ci-4.0-dev/system/HTTP/ContentSecurityPolicy.php new file mode 100644 index 000000000..8c15f83b7 --- /dev/null +++ b/ci-4.0-dev/system/HTTP/ContentSecurityPolicy.php @@ -0,0 +1,759 @@ + $value) + { + if (isset($this->{$setting})) + { + $this->{$setting} = $value; + } + } + } + + //-------------------------------------------------------------------- + + /** + * Compiles and sets the appropriate headers in the request. + * + * Should be called just prior to sending the response to the user agent. + * + * @param ResponseInterface $response + */ + public function finalize(ResponseInterface &$response) + { + $this->generateNonces($response); + + $this->buildHeaders($response); + } + + //-------------------------------------------------------------------- + + //-------------------------------------------------------------------- + // Setters + //-------------------------------------------------------------------- + + /** + * If TRUE, nothing will be restricted. Instead all violations will + * be reported to the reportURI for monitoring. This is useful when + * you are just starting to implement the policy, and will help + * determine what errors need to be addressed before you turn on + * all filtering. + * + * @param bool|true $value + * + * @return $this + */ + public function reportOnly(bool $value = true) + { + $this->reportOnly = $value; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Sets the base_uri value. Can be either a URI class or a simple string. + * + * base_uri restricts the URLs that can appear in a page’s element. + * + * @see http://www.w3.org/TR/CSP/#directive-base-uri + * + * @param string $uri + * @param bool $reportOnly + * + * @return $this + */ + public function setBaseURI($uri, bool $reportOnly) + { + $this->baseURI = [(string)$uri => $reportOnly]; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Adds a new valid endpoint for a form's action. Can be either + * a URI class or a simple string. + * + * child-src lists the URLs for workers and embedded frame contents. + * For example: child-src https://youtube.com would enable embedding + * videos from YouTube but not from other origins. + * + * @see http://www.w3.org/TR/CSP/#directive-child-src + * + * @param $uri + * @param bool $reportOnly + * + * @return $this + */ + public function addChildSrc($uri, bool $reportOnly = false) + { + $this->addOption($uri, 'childSrc', $reportOnly); + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Adds a new valid endpoint for a form's action. Can be either + * a URI class or a simple string. + * + * connect-src limits the origins to which you can connect + * (via XHR, WebSockets, and EventSource). + * + * @see http://www.w3.org/TR/CSP/#directive-connect-src + * + * @param $uri + * @param bool $reportOnly + * + * @return $this + */ + public function addConnectSrc($uri, bool $reportOnly = false) + { + $this->addOption($uri, 'connectSrc', $reportOnly); + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Adds a new valid endpoint for a form's action. Can be either + * a URI class or a simple string. + * + * default_src is the URI that is used for many of the settings when + * no other source has been set. + * + * @see http://www.w3.org/TR/CSP/#directive-default-src + * + * @param $uri + * @param bool $reportOnly + * + * @return $this + */ + public function setDefaultSrc($uri, bool $reportOnly = false) + { + $this->defaultSrc = [(string)$uri => $reportOnly]; + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Adds a new valid endpoint for a form's action. Can be either + * a URI class or a simple string. + * + * font-src specifies the origins that can serve web fonts. + * + * @see http://www.w3.org/TR/CSP/#directive-font-src + * + * @param $uri + * @param bool $reportOnly + * + * @return $this + */ + public function addFontSrc($uri, bool $reportOnly = false) + { + $this->addOption($uri, 'fontSrc', $reportOnly); + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Adds a new valid endpoint for a form's action. Can be either + * a URI class or a simple string. + * + * @see http://www.w3.org/TR/CSP/#directive-form-action + * + * @param $uri + * @param bool $reportOnly + * + * @return $this + */ + public function addFormAction($uri, bool $reportOnly = false) + { + $this->addOption($uri, 'formAction', $reportOnly); + + return $this; + } + + //-------------------------------------------------------------------- + + /** + * Adds a new resource that should allow embedding the resource using + * ,