commit 082a0130c295668d4c31da3dacc68cfd92a5953a Author: Polonkai Gergely Date: Sun Jul 1 09:52:20 2012 +0200 Initial commit with Symfony 2.1+Vendors Signed-off-by: Gergely POLONKAI (W00d5t0ck) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cdffe7a --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2012 Fabien Potencier + +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/README.md b/README.md new file mode 100644 index 0000000..52ee084 --- /dev/null +++ b/README.md @@ -0,0 +1,174 @@ +Symfony Standard Edition +======================== + +Welcome to the Symfony Standard Edition - a fully-functional Symfony2 +application that you can use as the skeleton for your new applications. + +This document contains information on how to download, install, and start +using Symfony. For a more detailed explanation, see the [Installation][1] +chapter of the Symfony Documentation. + +1) Installing the Standard Edition +---------------------------------- + +When it comes to installing the Symfony Standard Edition, you have the +following options. + +### Use Composer (*recommended*) + +As Symfony uses [Composer][2] to manage its dependencies, the recommended way +to create a new project is to use it. + +If you don't have Composer yet, download it following the instructions on +http://getcomposer.org/ or just run the following command: + + curl -s http://getcomposer.org/installer | php + +Then, use the `create-project` command to generate a new Symfony application: + + composer.phar create-project symfony/framework-standard-edition path/to/install + +Composer will install Symfony and all its dependencies under the +`path/to/install` directory. + +### Download an Archive File + +To quickly test Symfony, you can also download an [archive][3] of the Standard +Edition and unpack it somewhere under your web server root directory. + +If you downloaded an archive "without vendors", you also need to install all +the necessary dependencies. Download composer (see above) and run the +following command: + + php composer.phar install + +2) Checking your System Configuration +------------------------------------- + +Before starting coding, make sure that your local system is properly +configured for Symfony. + +Execute the `check.php` script from the command line: + + php app/check.php + +Access the `config.php` script from a browser: + + http://localhost/path/to/symfony/app/web/config.php + +If you get any warnings or recommendations, fix them before moving on. + +3) Browsing the Demo Application +-------------------------------- + +Congratulations! You're now ready to use Symfony. + +From the `config.php` page, click the "Bypass configuration and go to the +Welcome page" link to load up your first Symfony page. + +You can also use a web-based configurator by clicking on the "Configure your +Symfony Application online" link of the `config.php` page. + +To see a real-live Symfony page in action, access the following page: + + web/app_dev.php/demo/hello/Fabien + +3) Getting started with Symfony +------------------------------- + +This distribution is meant to be the starting point for your Symfony +applications, but it also contains some sample code that you can learn from +and play with. + +A great way to start learning Symfony is via the [Quick Tour][4], which will +take you through all the basic features of Symfony2. + +Once you're feeling good, you can move onto reading the official +[Symfony2 book][5]. + +A default bundle, `AcmeDemoBundle`, shows you Symfony2 in action. After +playing with it, you can remove it by following these steps: + + * delete the `src/Acme` directory; + + * remove the routing entries referencing AcmeBundle in + `app/config/routing_dev.yml`; + + * remove the AcmeBundle from the registered bundles in `app/AppKernel.php`; + + * remove the `web/bundles/acmedemo` directory; + + * remove the inclusion of the security configuration in + `app/config/config.yml` (remove the `- { resource: security.yml }` line) + or tweak the security configuration to fit your needs. + +What's inside? +--------------- + +The Symfony Standard Edition is configured with the following defaults: + + * Twig is the only configured template engine; + + * Doctrine ORM/DBAL is configured; + + * Swiftmailer is configured; + + * Annotations for everything are enabled. + +It comes pre-configured with the following bundles: + + * **FrameworkBundle** - The core Symfony framework bundle + + * [**SensioFrameworkExtraBundle**][6] - Adds several enhancements, including + template and routing annotation capability + + * [**DoctrineBundle**][7] - Adds support for the Doctrine ORM + + * [**TwigBundle**][8] - Adds support for the Twig templating engine + + * [**SecurityBundle**][9] - Adds security by integrating Symfony's security + component + + * [**SwiftmailerBundle**][10] - Adds support for Swiftmailer, a library for + sending emails + + * [**MonologBundle**][11] - Adds support for Monolog, a logging library + + * [**AsseticBundle**][12] - Adds support for Assetic, an asset processing + library + + * [**JMSSecurityExtraBundle**][13] - Allows security to be added via + annotations + + * [**JMSDiExtraBundle**][14] - Adds more powerful dependency injection + features + + * **WebProfilerBundle** (in dev/test env) - Adds profiling functionality and + the web debug toolbar + + * **SensioDistributionBundle** (in dev/test env) - Adds functionality for + configuring and working with Symfony distributions + + * [**SensioGeneratorBundle**][15] (in dev/test env) - Adds code generation + capabilities + + * **AcmeDemoBundle** (in dev/test env) - A demo bundle with some example + code + +Enjoy! + +[1]: http://symfony.com/doc/2.1/book/installation.html +[2]: http://getcomposer.org/ +[3]: http://symfony.com/download +[4]: http://symfony.com/doc/2.1/quick_tour/the_big_picture.html +[5]: http://symfony.com/doc/2.1/ +[6]: http://symfony.com/doc/2.1/bundles/SensioFrameworkExtraBundle/index.html +[7]: http://symfony.com/doc/2.1/book/doctrine.html +[8]: http://symfony.com/doc/2.1/book/templating.html +[9]: http://symfony.com/doc/2.1/book/security.html +[10]: http://symfony.com/doc/2.1/cookbook/email.html +[11]: http://symfony.com/doc/2.1/cookbook/logging/monolog.html +[12]: http://symfony.com/doc/2.1/cookbook/assetic/asset_management.html +[13]: http://jmsyst.com/bundles/JMSSecurityExtraBundle/1.1 +[14]: http://jmsyst.com/bundles/JMSDiExtraBundle/1.0 +[15]: http://symfony.com/doc/2.1/bundles/SensioGeneratorBundle/index.html diff --git a/app/.htaccess b/app/.htaccess new file mode 100644 index 0000000..3418e55 --- /dev/null +++ b/app/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/app/AppCache.php b/app/AppCache.php new file mode 100644 index 0000000..ddb51db --- /dev/null +++ b/app/AppCache.php @@ -0,0 +1,9 @@ +getEnvironment(), array('dev', 'test'))) { + $bundles[] = new Acme\DemoBundle\AcmeDemoBundle(); + $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); + $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle(); + $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(); + } + + return $bundles; + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml'); + } +} diff --git a/app/Resources/views/base.html.twig b/app/Resources/views/base.html.twig new file mode 100644 index 0000000..bafd28d --- /dev/null +++ b/app/Resources/views/base.html.twig @@ -0,0 +1,13 @@ + + + + + {% block title %}Welcome!{% endblock %} + {% block stylesheets %}{% endblock %} + + + + {% block body %}{% endblock %} + {% block javascripts %}{% endblock %} + + diff --git a/app/SymfonyRequirements.php b/app/SymfonyRequirements.php new file mode 100644 index 0000000..0399b96 --- /dev/null +++ b/app/SymfonyRequirements.php @@ -0,0 +1,580 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Represents a single PHP requirement, e.g. an installed extension. + * It can be a mandatory requirement or an optional recommendation. + * There is a special subclass, named PhpIniRequirement, to check a php.ini configuration. + * + * @author Tobias Schultze + */ +class Requirement +{ + private $fulfilled; + private $testMessage; + private $helpText; + private $helpHtml; + private $optional; + + /** + * Constructor that initializes the requirement. + * + * @param Boolean $fulfilled Whether the requirement is fulfilled + * @param string $testMessage The message for testing the requirement + * @param string $helpHtml The help text formatted in HTML for resolving the problem + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param Boolean $optional Whether this is only an optional recommendation not a mandatory requirement + */ + public function __construct($fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false) + { + $this->fulfilled = (Boolean) $fulfilled; + $this->testMessage = (string) $testMessage; + $this->helpHtml = (string) $helpHtml; + $this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string) $helpText; + $this->optional = (Boolean) $optional; + } + + /** + * Returns whether the requirement is fulfilled. + * + * @return Boolean true if fulfilled, otherwise false + */ + public function isFulfilled() + { + return $this->fulfilled; + } + + /** + * Returns the message for testing the requirement. + * + * @return string The test message + */ + public function getTestMessage() + { + return $this->testMessage; + } + + /** + * Returns the help text for resolving the problem + * + * @return string The help text + */ + public function getHelpText() + { + return $this->helpText; + } + + /** + * Returns the help text formatted in HTML. + * + * @return string The HTML help + */ + public function getHelpHtml() + { + return $this->helpHtml; + } + + /** + * Returns whether this is only an optional recommendation and not a mandatory requirement. + * + * @return Boolean true if optional, false if mandatory + */ + public function isOptional() + { + return $this->optional; + } +} + +/** + * Represents a PHP requirement in form of a php.ini configuration. + * + * @author Tobias Schultze + */ +class PhpIniRequirement extends Requirement +{ + /** + * Constructor that initializes the requirement. + * + * @param string $cfgName The configuration name used for ini_get() + * @param Boolean|callback $evaluation Either a Boolean indicating whether the configuration should evaluate to true or false, + or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement + * @param Boolean $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. + Example: You require a config to be true but PHP later removes this config and defaults it to true internally. + * @param string $testMessage The message for testing the requirement (when null and $evaluation is a Boolean a default message is derived) + * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a Boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param Boolean $optional Whether this is only an optional recommendation not a mandatory requirement + */ + public function __construct($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null, $optional = false) + { + $cfgValue = ini_get($cfgName); + + if (is_callable($evaluation)) { + if (null === $testMessage || null === $helpHtml) { + throw new InvalidArgumentException('You must provide the parameters testMessage and helpHtml for a callback evaluation.'); + } + + $fulfilled = call_user_func($evaluation, $cfgValue); + } else { + if (null === $testMessage) { + $testMessage = sprintf('%s %s be %s in php.ini', + $cfgName, + $optional ? 'should' : 'must', + $evaluation ? 'enabled' : 'disabled' + ); + } + + if (null === $helpHtml) { + $helpHtml = sprintf('Set %s to %s in php.ini*.', + $cfgName, + $evaluation ? 'on' : 'off' + ); + } + + $fulfilled = $evaluation == $cfgValue; + } + + parent::__construct($fulfilled || ($approveCfgAbsence && false === $cfgValue), $testMessage, $helpHtml, $helpText, $optional); + } +} + +/** + * A RequirementCollection represents a set of Requirement instances. + * + * Users of PHP 5.2 should be able to run the requirements checks. + * This is why the class must be compatible with PHP 5.2 + * (e.g. not using namespaces and closures). + * + * @author Tobias Schultze + */ +class RequirementCollection implements IteratorAggregate +{ + private $requirements = array(); + + /** + * Gets the current RequirementCollection as an Iterator. + * + * @return Traversable A Traversable interface + */ + public function getIterator() + { + return new ArrayIterator($this->requirements); + } + + /** + * Adds a Requirement. + * + * @param Requirement $requirement A Requirement instance + */ + public function add(Requirement $requirement) + { + $this->requirements[] = $requirement; + } + + /** + * Adds a mandatory requirement. + * + * @param Boolean $fulfilled Whether the requirement is fulfilled + * @param string $testMessage The message for testing the requirement + * @param string $helpHtml The help text formatted in HTML for resolving the problem + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addRequirement($fulfilled, $testMessage, $helpHtml, $helpText = null) + { + $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, false)); + } + + /** + * Adds an optional recommendation. + * + * @param Boolean $fulfilled Whether the recommendation is fulfilled + * @param string $testMessage The message for testing the recommendation + * @param string $helpHtml The help text formatted in HTML for resolving the problem + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addRecommendation($fulfilled, $testMessage, $helpHtml, $helpText = null) + { + $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, true)); + } + + /** + * Adds a mandatory requirement in form of a php.ini configuration. + * + * @param string $cfgName The configuration name used for ini_get() + * @param Boolean|callback $evaluation Either a Boolean indicating whether the configuration should evaluate to true or false, + or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement + * @param Boolean $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. + Example: You require a config to be true but PHP later removes this config and defaults it to true internally. + * @param string $testMessage The message for testing the requirement (when null and $evaluation is a Boolean a default message is derived) + * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a Boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addPhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null) + { + $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, false)); + } + + /** + * Adds an optional recommendation in form of a php.ini configuration. + * + * @param string $cfgName The configuration name used for ini_get() + * @param Boolean|callback $evaluation Either a Boolean indicating whether the configuration should evaluate to true or false, + or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement + * @param Boolean $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. + Example: You require a config to be true but PHP later removes this config and defaults it to true internally. + * @param string $testMessage The message for testing the requirement (when null and $evaluation is a Boolean a default message is derived) + * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a Boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addPhpIniRecommendation($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null) + { + $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, true)); + } + + /** + * Adds a requirement collection to the current set of requirements. + * + * @param RequirementCollection $collection A RequirementCollection instance + */ + public function addCollection(RequirementCollection $collection) + { + $this->requirements = array_merge($this->requirements, $collection->all()); + } + + /** + * Returns both requirements and recommendations. + * + * @return array Array of Requirement instances + */ + public function all() + { + return $this->requirements; + } + + /** + * Returns all mandatory requirements. + * + * @return array Array of Requirement instances + */ + public function getRequirements() + { + $array = array(); + foreach ($this->requirements as $req) { + if (!$req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns the mandatory requirements that were not met. + * + * @return array Array of Requirement instances + */ + public function getFailedRequirements() + { + $array = array(); + foreach ($this->requirements as $req) { + if (!$req->isFulfilled() && !$req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns all optional recommmendations. + * + * @return array Array of Requirement instances + */ + public function getRecommendations() + { + $array = array(); + foreach ($this->requirements as $req) { + if ($req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns the recommendations that were not met. + * + * @return array Array of Requirement instances + */ + public function getFailedRecommendations() + { + $array = array(); + foreach ($this->requirements as $req) { + if (!$req->isFulfilled() && $req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns whether a php.ini configuration is not correct. + * + * @return Boolean php.ini configuration problem? + */ + public function hasPhpIniConfigIssue() + { + foreach ($this->requirements as $req) { + if (!$req->isFulfilled() && $req instanceof PhpIniRequirement) { + return true; + } + } + + return false; + } + + /** + * Returns the PHP configuration file (php.ini) path. + * + * @return string|false php.ini file path + */ + public function getPhpIniConfigPath() + { + return get_cfg_var('cfg_file_path'); + } +} + +/** + * This class specifies all requirements and optional recommendations that + * are necessary to run the Symfony Standard Edition. + * + * @author Tobias Schultze + */ +class SymfonyRequirements extends RequirementCollection +{ + const REQUIRED_PHP_VERSION = '5.3.3'; + + /** + * Constructor that initializes the requirements. + */ + public function __construct() + { + /* mandatory requirements follow */ + + $installedPhpVersion = phpversion(); + + $this->addRequirement( + version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>='), + sprintf('PHP version must be at least %s (%s installed)', self::REQUIRED_PHP_VERSION, $installedPhpVersion), + sprintf('You are running PHP version "%s", but Symfony needs at least PHP "%s" to run. + Before using Symfony, upgrade your PHP installation, preferably to the latest version.', + $installedPhpVersion, self::REQUIRED_PHP_VERSION), + sprintf('Install PHP %s or newer (installed version is %s)', self::REQUIRED_PHP_VERSION, $installedPhpVersion) + ); + + $this->addRequirement( + is_dir(__DIR__.'/../vendor/symfony'), + 'Vendor libraries must be installed', + 'Vendor libraries are missing. Install composer following instructions from http://getcomposer.org/. ' . + 'Then run "php composer.phar install" to install them.' + ); + + $baseDir = basename(__DIR__); + $this->addRequirement( + is_writable(__DIR__.'/cache'), + "$baseDir/cache/ directory must be writable", + "Change the permissions of the \"$baseDir/cache/\" directory so that the web server can write into it." + ); + + $this->addRequirement( + is_writable(__DIR__.'/logs'), + "$baseDir/logs/ directory must be writable", + "Change the permissions of the \"$baseDir/logs/\" directory so that the web server can write into it." + ); + + $this->addPhpIniRequirement( + 'date.timezone', true, false, + 'date.timezone setting must be set', + 'Set the "date.timezone" setting in php.ini* (like Europe/Paris).' + ); + + if (version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>=')) { + $this->addRequirement( + (in_array(date_default_timezone_get(), \DateTimeZone::listIdentifiers())), + sprintf('Default timezone is deprecated (%s)', date_default_timezone_get()), + 'Fix your php.ini file (list of deprecated timezones http://us.php.net/manual/en/timezones.others.php).' + ); + } + + $this->addRequirement( + function_exists('json_encode'), + 'json_encode() must be available', + 'Install and enable the JSON extension.' + ); + + $this->addRequirement( + function_exists('session_start'), + 'session_start() must be available', + 'Install and enable the session extension.' + ); + + $this->addRequirement( + function_exists('ctype_alpha'), + 'ctype_alpha() must be available', + 'Install and enable the ctype extension.' + ); + + $this->addRequirement( + function_exists('token_get_all'), + 'token_get_all() must be available', + 'Install and enable the Tokenizer extension.' + ); + + $this->addRequirement( + function_exists('simplexml_import_dom'), + 'simplexml_import_dom() must be available', + 'Install and enable the SimpleXML extension.' + ); + + $this->addRequirement( + !(function_exists('apc_store') && ini_get('apc.enabled')) || version_compare(phpversion('apc'), '3.0.17', '>='), + 'APC version must be at least 3.0.17', + 'Upgrade your APC extension (3.0.17+)' + ); + + $this->addPhpIniRequirement('detect_unicode', false); + + $this->addPhpIniRequirement( + 'suhosin.executor.include.whitelist', + create_function('$cfgValue', 'return false !== stripos($cfgValue, "phar");'), + true, + 'suhosin.executor.include.whitelist must be configured correctly in php.ini', + 'Add "phar" to suhosin.executor.include.whitelist in php.ini*.' + ); + + $pcreVersion = defined('PCRE_VERSION') ? (float) PCRE_VERSION : null; + + $this->addRequirement( + null !== $pcreVersion && $pcreVersion > 8.0, + sprintf('PCRE extension must be available and at least 8.0 (%s installed)', $pcreVersion ? $pcreVersion : 'not'), + 'Upgrade your PCRE extension (8.0+)' + ); + + /* optional recommendations follow */ + + $this->addRecommendation( + version_compare($installedPhpVersion, '5.3.8', '>='), + sprintf('Annotations might not work properly due to the PHP bug #55156 before PHP 5.3.8 (%s installed)', $installedPhpVersion), + 'Install PHP 5.3.8 or newer if your project uses annotations' + ); + + $this->addRecommendation( + class_exists('DomDocument'), + 'PHP-XML module should be installed', + 'Install and enable the PHP-XML module.' + ); + + $this->addRecommendation( + function_exists('mb_strlen'), + 'mb_strlen() should be available', + 'Install and enable the mbstring extension.' + ); + + $this->addRecommendation( + function_exists('iconv'), + 'iconv() should be available', + 'Install and enable the iconv extension.' + ); + + $this->addRecommendation( + function_exists('utf8_decode'), + 'utf8_decode() should be available', + 'Install and enable the XML extension.' + ); + + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->addRecommendation( + function_exists('posix_isatty'), + 'posix_isatty() should be available', + 'Install and enable the php_posix extension (used to colorize the CLI output).' + ); + } + + $this->addRecommendation( + class_exists('Locale'), + 'intl extension should be available', + 'Install and enable the intl extension (used for validators).' + ); + + if (class_exists('Locale')) { + if (defined('INTL_ICU_VERSION')) { + $version = INTL_ICU_VERSION; + } else { + $reflector = new ReflectionExtension('intl'); + + ob_start(); + $reflector->info(); + $output = strip_tags(ob_get_clean()); + + preg_match('/^ICU version +(?:=> )?(.*)$/m', $output, $matches); + $version = $matches[1]; + } + + $this->addRecommendation( + version_compare($version, '4.0', '>='), + 'intl ICU version should be at least 4+', + 'Upgrade your intl extension with a newer ICU version (4+).' + ); + } + + $accelerator = + (function_exists('apc_store') && ini_get('apc.enabled')) + || + function_exists('eaccelerator_put') && ini_get('eaccelerator.enable') + || + function_exists('xcache_set') + ; + + $this->addRecommendation( + $accelerator, + 'a PHP accelerator should be installed', + 'Install and enable a PHP accelerator like APC (highly recommended).' + ); + + $this->addPhpIniRecommendation('short_open_tag', false); + + $this->addPhpIniRecommendation('magic_quotes_gpc', false, true); + + $this->addPhpIniRecommendation('register_globals', false, true); + + $this->addPhpIniRecommendation('session.auto_start', false); + + $this->addRecommendation( + class_exists('PDO'), + 'PDO should be installed', + 'Install PDO (mandatory for Doctrine).' + ); + + if (class_exists('PDO')) { + $drivers = PDO::getAvailableDrivers(); + $this->addRecommendation( + count($drivers), + sprintf('PDO should have some drivers installed (currently available: %s)', count($drivers) ? implode(', ', $drivers) : 'none'), + 'Install PDO drivers (mandatory for Doctrine).' + ); + } + } +} diff --git a/app/autoload.php b/app/autoload.php new file mode 100644 index 0000000..28425dc --- /dev/null +++ b/app/autoload.php @@ -0,0 +1,32 @@ +You must set up the project dependencies by running the following commands:

+
+    curl -s http://getcomposer.org/installer | php
+    php composer.phar install
+
+ +EOF; + + if (PHP_SAPI === 'cli') { + $message = strip_tags($message); + } + + die($message); +} + +// intl +if (!function_exists('intl_get_error_code')) { + require_once __DIR__.'/../vendor/symfony/symfony/src/Symfony/Component/Locale/Resources/stubs/functions.php'; + + $loader->add('', __DIR__.'/../vendor/symfony/symfony/src/Symfony/Component/Locale/Resources/stubs'); +} + +AnnotationRegistry::registerLoader(array($loader, 'loadClass')); + +return $loader; diff --git a/app/bootstrap.php.cache b/app/bootstrap.php.cache new file mode 100644 index 0000000..d15c95a --- /dev/null +++ b/app/bootstrap.php.cache @@ -0,0 +1,1455 @@ +parameterBag = null === $parameterBag ? new ParameterBag() : $parameterBag; + + $this->services = array(); + $this->scopes = array(); + $this->scopeChildren = array(); + $this->scopedServices = array(); + $this->scopeStacks = array(); + + $this->set('service_container', $this); + } + + + public function compile() + { + $this->parameterBag->resolve(); + + $this->parameterBag = new FrozenParameterBag($this->parameterBag->all()); + } + + + public function isFrozen() + { + return $this->parameterBag instanceof FrozenParameterBag; + } + + + public function getParameterBag() + { + return $this->parameterBag; + } + + + public function getParameter($name) + { + return $this->parameterBag->get($name); + } + + + public function hasParameter($name) + { + return $this->parameterBag->has($name); + } + + + public function setParameter($name, $value) + { + $this->parameterBag->set($name, $value); + } + + + public function set($id, $service, $scope = self::SCOPE_CONTAINER) + { + if (self::SCOPE_PROTOTYPE === $scope) { + throw new InvalidArgumentException('You cannot set services of scope "prototype".'); + } + + $id = strtolower($id); + + if (self::SCOPE_CONTAINER !== $scope) { + if (!isset($this->scopedServices[$scope])) { + throw new RuntimeException('You cannot set services of inactive scopes.'); + } + + $this->scopedServices[$scope][$id] = $service; + } + + $this->services[$id] = $service; + } + + + public function has($id) + { + $id = strtolower($id); + + return isset($this->services[$id]) || method_exists($this, 'get'.strtr($id, array('_' => '', '.' => '_')).'Service'); + } + + + public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) + { + $id = strtolower($id); + + if (isset($this->services[$id])) { + return $this->services[$id]; + } + + if (isset($this->loading[$id])) { + throw new ServiceCircularReferenceException($id, array_keys($this->loading)); + } + + if (method_exists($this, $method = 'get'.strtr($id, array('_' => '', '.' => '_')).'Service')) { + $this->loading[$id] = true; + + try { + $service = $this->$method(); + } catch (\Exception $e) { + unset($this->loading[$id]); + throw $e; + } + + unset($this->loading[$id]); + + return $service; + } + + if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { + throw new ServiceNotFoundException($id); + } + } + + + public function initialized($id) + { + return isset($this->services[strtolower($id)]); + } + + + public function getServiceIds() + { + $ids = array(); + $r = new \ReflectionClass($this); + foreach ($r->getMethods() as $method) { + if (preg_match('/^get(.+)Service$/', $method->name, $match)) { + $ids[] = self::underscore($match[1]); + } + } + + return array_unique(array_merge($ids, array_keys($this->services))); + } + + + public function enterScope($name) + { + if (!isset($this->scopes[$name])) { + throw new InvalidArgumentException(sprintf('The scope "%s" does not exist.', $name)); + } + + if (self::SCOPE_CONTAINER !== $this->scopes[$name] && !isset($this->scopedServices[$this->scopes[$name]])) { + throw new RuntimeException(sprintf('The parent scope "%s" must be active when entering this scope.', $this->scopes[$name])); + } + + if (isset($this->scopedServices[$name])) { + $services = array($this->services, $name => $this->scopedServices[$name]); + unset($this->scopedServices[$name]); + + foreach ($this->scopeChildren[$name] as $child) { + $services[$child] = $this->scopedServices[$child]; + unset($this->scopedServices[$child]); + } + + $this->services = call_user_func_array('array_diff_key', $services); + array_shift($services); + + if (!isset($this->scopeStacks[$name])) { + $this->scopeStacks[$name] = new \SplStack(); + } + $this->scopeStacks[$name]->push($services); + } + + $this->scopedServices[$name] = array(); + } + + + public function leaveScope($name) + { + if (!isset($this->scopedServices[$name])) { + throw new InvalidArgumentException(sprintf('The scope "%s" is not active.', $name)); + } + + $services = array($this->services, $this->scopedServices[$name]); + unset($this->scopedServices[$name]); + foreach ($this->scopeChildren[$name] as $child) { + if (!isset($this->scopedServices[$child])) { + continue; + } + + $services[] = $this->scopedServices[$child]; + unset($this->scopedServices[$child]); + } + $this->services = call_user_func_array('array_diff_key', $services); + + if (isset($this->scopeStacks[$name]) && count($this->scopeStacks[$name]) > 0) { + $services = $this->scopeStacks[$name]->pop(); + $this->scopedServices += $services; + + array_unshift($services, $this->services); + $this->services = call_user_func_array('array_merge', $services); + } + } + + + public function addScope(ScopeInterface $scope) + { + $name = $scope->getName(); + $parentScope = $scope->getParentName(); + + if (self::SCOPE_CONTAINER === $name || self::SCOPE_PROTOTYPE === $name) { + throw new InvalidArgumentException(sprintf('The scope "%s" is reserved.', $name)); + } + if (isset($this->scopes[$name])) { + throw new InvalidArgumentException(sprintf('A scope with name "%s" already exists.', $name)); + } + if (self::SCOPE_CONTAINER !== $parentScope && !isset($this->scopes[$parentScope])) { + throw new InvalidArgumentException(sprintf('The parent scope "%s" does not exist, or is invalid.', $parentScope)); + } + + $this->scopes[$name] = $parentScope; + $this->scopeChildren[$name] = array(); + + while ($parentScope !== self::SCOPE_CONTAINER) { + $this->scopeChildren[$parentScope][] = $name; + $parentScope = $this->scopes[$parentScope]; + } + } + + + public function hasScope($name) + { + return isset($this->scopes[$name]); + } + + + public function isScopeActive($name) + { + return isset($this->scopedServices[$name]); + } + + + static public function camelize($id) + { + return preg_replace_callback('/(^|_|\.)+(.)/', function ($match) { return ('.' === $match[1] ? '_' : '').strtoupper($match[2]); }, $id); + } + + + static public function underscore($id) + { + return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($id, '_', '.'))); + } +} +} + + + + +namespace Symfony\Component\HttpKernel +{ + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + + +interface HttpKernelInterface +{ + const MASTER_REQUEST = 1; + const SUB_REQUEST = 2; + + + function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true); +} +} + + + + +namespace Symfony\Component\HttpKernel +{ + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\Config\Loader\LoaderInterface; + + +interface KernelInterface extends HttpKernelInterface, \Serializable +{ + + function registerBundles(); + + + function registerContainerConfiguration(LoaderInterface $loader); + + + function boot(); + + + function shutdown(); + + + function getBundles(); + + + function isClassInActiveBundle($class); + + + function getBundle($name, $first = true); + + + function locateResource($name, $dir = null, $first = true); + + + function getName(); + + + function getEnvironment(); + + + function isDebug(); + + + function getRootDir(); + + + function getContainer(); + + + function getStartTime(); + + + function getCacheDir(); + + + function getLogDir(); +} +} + + + + +namespace Symfony\Component\HttpKernel +{ + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\PhpDumper; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Loader\ClosureLoader; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\HttpKernel\Config\FileLocator; +use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; +use Symfony\Component\HttpKernel\DependencyInjection\AddClassesToCachePass; +use Symfony\Component\HttpKernel\Debug\ErrorHandler; +use Symfony\Component\HttpKernel\Debug\ExceptionHandler; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\Loader\DelegatingLoader; +use Symfony\Component\Config\ConfigCache; +use Symfony\Component\ClassLoader\ClassCollectionLoader; +use Symfony\Component\ClassLoader\DebugClassLoader; + + +abstract class Kernel implements KernelInterface, TerminableInterface +{ + protected $bundles; + protected $bundleMap; + protected $container; + protected $rootDir; + protected $environment; + protected $debug; + protected $booted; + protected $name; + protected $startTime; + protected $classes; + protected $errorReportingLevel; + + const VERSION = '2.1.0-BETA1'; + const VERSION_ID = '20100'; + const MAJOR_VERSION = '2'; + const MINOR_VERSION = '1'; + const RELEASE_VERSION = '0'; + const EXTRA_VERSION = 'BETA'; + + + public function __construct($environment, $debug) + { + $this->environment = $environment; + $this->debug = (Boolean) $debug; + $this->booted = false; + $this->rootDir = $this->getRootDir(); + $this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir)); + $this->classes = array(); + + if ($this->debug) { + $this->startTime = microtime(true); + } + + $this->init(); + } + + public function init() + { + if ($this->debug) { + ini_set('display_errors', 1); + error_reporting(-1); + + DebugClassLoader::enable(); + ErrorHandler::register($this->errorReportingLevel); + if ('cli' !== php_sapi_name()) { + ExceptionHandler::register(); + } + } else { + ini_set('display_errors', 0); + } + } + + public function __clone() + { + if ($this->debug) { + $this->startTime = microtime(true); + } + + $this->booted = false; + $this->container = null; + } + + + public function boot() + { + if (true === $this->booted) { + return; + } + + $this->initializeBundles(); + + $this->initializeContainer(); + + foreach ($this->getBundles() as $bundle) { + $bundle->setContainer($this->container); + $bundle->boot(); + } + + $this->booted = true; + } + + + public function terminate(Request $request, Response $response) + { + if (false === $this->booted) { + return; + } + + if ($this->getHttpKernel() instanceof TerminableInterface) { + $this->getHttpKernel()->terminate($request, $response); + } + } + + + public function shutdown() + { + if (false === $this->booted) { + return; + } + + $this->booted = false; + + foreach ($this->getBundles() as $bundle) { + $bundle->shutdown(); + $bundle->setContainer(null); + } + + $this->container = null; + } + + + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) + { + if (false === $this->booted) { + $this->boot(); + } + + return $this->getHttpKernel()->handle($request, $type, $catch); + } + + + protected function getHttpKernel() + { + return $this->container->get('http_kernel'); + } + + + public function getBundles() + { + return $this->bundles; + } + + + public function isClassInActiveBundle($class) + { + foreach ($this->getBundles() as $bundle) { + if (0 === strpos($class, $bundle->getNamespace())) { + return true; + } + } + + return false; + } + + + public function getBundle($name, $first = true) + { + if (!isset($this->bundleMap[$name])) { + throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled. Maybe you forgot to add it in the registerBundles() function of your %s.php file?', $name, get_class($this))); + } + + if (true === $first) { + return $this->bundleMap[$name][0]; + } + + return $this->bundleMap[$name]; + } + + + public function locateResource($name, $dir = null, $first = true) + { + if ('@' !== $name[0]) { + throw new \InvalidArgumentException(sprintf('A resource name must start with @ ("%s" given).', $name)); + } + + if (false !== strpos($name, '..')) { + throw new \RuntimeException(sprintf('File name "%s" contains invalid characters (..).', $name)); + } + + $bundleName = substr($name, 1); + $path = ''; + if (false !== strpos($bundleName, '/')) { + list($bundleName, $path) = explode('/', $bundleName, 2); + } + + $isResource = 0 === strpos($path, 'Resources') && null !== $dir; + $overridePath = substr($path, 9); + $resourceBundle = null; + $bundles = $this->getBundle($bundleName, false); + $files = array(); + + foreach ($bundles as $bundle) { + if ($isResource && file_exists($file = $dir.'/'.$bundle->getName().$overridePath)) { + if (null !== $resourceBundle) { + throw new \RuntimeException(sprintf('"%s" resource is hidden by a resource from the "%s" derived bundle. Create a "%s" file to override the bundle resource.', + $file, + $resourceBundle, + $dir.'/'.$bundles[0]->getName().$overridePath + )); + } + + if ($first) { + return $file; + } + $files[] = $file; + } + + if (file_exists($file = $bundle->getPath().'/'.$path)) { + if ($first && !$isResource) { + return $file; + } + $files[] = $file; + $resourceBundle = $bundle->getName(); + } + } + + if (count($files) > 0) { + return $first && $isResource ? $files[0] : $files; + } + + throw new \InvalidArgumentException(sprintf('Unable to find file "%s".', $name)); + } + + + public function getName() + { + return $this->name; + } + + + public function getEnvironment() + { + return $this->environment; + } + + + public function isDebug() + { + return $this->debug; + } + + + public function getRootDir() + { + if (null === $this->rootDir) { + $r = new \ReflectionObject($this); + $this->rootDir = str_replace('\\', '/', dirname($r->getFileName())); + } + + return $this->rootDir; + } + + + public function getContainer() + { + return $this->container; + } + + + public function loadClassCache($name = 'classes', $extension = '.php') + { + if (!$this->booted && is_file($this->getCacheDir().'/classes.map')) { + ClassCollectionLoader::load(include($this->getCacheDir().'/classes.map'), $this->getCacheDir(), $name, $this->debug, false, $extension); + } + } + + + public function setClassCache(array $classes) + { + file_put_contents($this->getCacheDir().'/classes.map', sprintf('debug ? $this->startTime : -INF; + } + + + public function getCacheDir() + { + return $this->rootDir.'/cache/'.$this->environment; + } + + + public function getLogDir() + { + return $this->rootDir.'/logs'; + } + + + protected function initializeBundles() + { + $this->bundles = array(); + $topMostBundles = array(); + $directChildren = array(); + + foreach ($this->registerBundles() as $bundle) { + $name = $bundle->getName(); + if (isset($this->bundles[$name])) { + throw new \LogicException(sprintf('Trying to register two bundles with the same name "%s"', $name)); + } + $this->bundles[$name] = $bundle; + + if ($parentName = $bundle->getParent()) { + if (isset($directChildren[$parentName])) { + throw new \LogicException(sprintf('Bundle "%s" is directly extended by two bundles "%s" and "%s".', $parentName, $name, $directChildren[$parentName])); + } + if ($parentName == $name) { + throw new \LogicException(sprintf('Bundle "%s" can not extend itself.', $name)); + } + $directChildren[$parentName] = $name; + } else { + $topMostBundles[$name] = $bundle; + } + } + + if (count($diff = array_values(array_diff(array_keys($directChildren), array_keys($this->bundles))))) { + throw new \LogicException(sprintf('Bundle "%s" extends bundle "%s", which is not registered.', $directChildren[$diff[0]], $diff[0])); + } + + $this->bundleMap = array(); + foreach ($topMostBundles as $name => $bundle) { + $bundleMap = array($bundle); + $hierarchy = array($name); + + while (isset($directChildren[$name])) { + $name = $directChildren[$name]; + array_unshift($bundleMap, $this->bundles[$name]); + $hierarchy[] = $name; + } + + foreach ($hierarchy as $bundle) { + $this->bundleMap[$bundle] = $bundleMap; + array_pop($bundleMap); + } + } + + } + + + protected function getContainerClass() + { + return $this->name.ucfirst($this->environment).($this->debug ? 'Debug' : '').'ProjectContainer'; + } + + + protected function getContainerBaseClass() + { + return 'Container'; + } + + + protected function initializeContainer() + { + $class = $this->getContainerClass(); + $cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug); + $fresh = true; + if (!$cache->isFresh()) { + $container = $this->buildContainer(); + $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass()); + + $fresh = false; + } + + require_once $cache; + + $this->container = new $class(); + $this->container->set('kernel', $this); + + if (!$fresh && $this->container->has('cache_warmer')) { + $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir')); + } + } + + + protected function getKernelParameters() + { + $bundles = array(); + foreach ($this->bundles as $name => $bundle) { + $bundles[$name] = get_class($bundle); + } + + return array_merge( + array( + 'kernel.root_dir' => $this->rootDir, + 'kernel.environment' => $this->environment, + 'kernel.debug' => $this->debug, + 'kernel.name' => $this->name, + 'kernel.cache_dir' => $this->getCacheDir(), + 'kernel.logs_dir' => $this->getLogDir(), + 'kernel.bundles' => $bundles, + 'kernel.charset' => 'UTF-8', + 'kernel.container_class' => $this->getContainerClass(), + ), + $this->getEnvParameters() + ); + } + + + protected function getEnvParameters() + { + $parameters = array(); + foreach ($_SERVER as $key => $value) { + if (0 === strpos($key, 'SYMFONY__')) { + $parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value; + } + } + + return $parameters; + } + + + protected function buildContainer() + { + foreach (array('cache' => $this->getCacheDir(), 'logs' => $this->getLogDir()) as $name => $dir) { + if (!is_dir($dir)) { + if (false === @mkdir($dir, 0777, true)) { + throw new \RuntimeException(sprintf("Unable to create the %s directory (%s)\n", $name, $dir)); + } + } elseif (!is_writable($dir)) { + throw new \RuntimeException(sprintf("Unable to write in the %s directory (%s)\n", $name, $dir)); + } + } + + $container = $this->getContainerBuilder(); + $extensions = array(); + foreach ($this->bundles as $bundle) { + if ($extension = $bundle->getContainerExtension()) { + $container->registerExtension($extension); + $extensions[] = $extension->getAlias(); + } + + if ($this->debug) { + $container->addObjectResource($bundle); + } + } + foreach ($this->bundles as $bundle) { + $bundle->build($container); + } + + $container->addObjectResource($this); + + $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions)); + + if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) { + $container->merge($cont); + } + + $container->addCompilerPass(new AddClassesToCachePass($this)); + $container->compile(); + + return $container; + } + + + protected function getContainerBuilder() + { + return new ContainerBuilder(new ParameterBag($this->getKernelParameters())); + } + + + protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container, $class, $baseClass) + { + $dumper = new PhpDumper($container); + $content = $dumper->dump(array('class' => $class, 'base_class' => $baseClass)); + if (!$this->debug) { + $content = self::stripComments($content); + } + + $cache->write($content, $container->getResources()); + } + + + protected function getContainerLoader(ContainerInterface $container) + { + $locator = new FileLocator($this); + $resolver = new LoaderResolver(array( + new XmlFileLoader($container, $locator), + new YamlFileLoader($container, $locator), + new IniFileLoader($container, $locator), + new PhpFileLoader($container, $locator), + new ClosureLoader($container), + )); + + return new DelegatingLoader($resolver); + } + + + static public function stripComments($source) + { + if (!function_exists('token_get_all')) { + return $source; + } + + $output = ''; + foreach (token_get_all($source) as $token) { + if (is_string($token)) { + $output .= $token; + } elseif (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) { + $output .= $token[1]; + } + } + + $output = preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $output); + + return $output; + } + + public function serialize() + { + return serialize(array($this->environment, $this->debug)); + } + + public function unserialize($data) + { + list($environment, $debug) = unserialize($data); + + $this->__construct($environment, $debug); + } +} +} + + + + +namespace Symfony\Component\ClassLoader +{ + + +class ClassCollectionLoader +{ + static private $loaded; + + + static public function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php') + { + if (isset(self::$loaded[$name])) { + return; + } + + self::$loaded[$name] = true; + + if ($adaptive) { + $classes = array_diff($classes, get_declared_classes(), get_declared_interfaces()); + + $name = $name.'-'.substr(md5(implode('|', $classes)), 0, 5); + } + + $cache = $cacheDir.'/'.$name.$extension; + + $reload = false; + if ($autoReload) { + $metadata = $cacheDir.'/'.$name.$extension.'.meta'; + if (!is_file($metadata) || !is_file($cache)) { + $reload = true; + } else { + $time = filemtime($cache); + $meta = unserialize(file_get_contents($metadata)); + + if ($meta[1] != $classes) { + $reload = true; + } else { + foreach ($meta[0] as $resource) { + if (!is_file($resource) || filemtime($resource) > $time) { + $reload = true; + + break; + } + } + } + } + } + + if (!$reload && is_file($cache)) { + require_once $cache; + + return; + } + + $files = array(); + $content = ''; + foreach ($classes as $class) { + if (!class_exists($class) && !interface_exists($class) && (!function_exists('trait_exists') || !trait_exists($class))) { + throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class)); + } + + $r = new \ReflectionClass($class); + $files[] = $r->getFileName(); + + $c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($r->getFileName())); + + if (!$r->inNamespace()) { + $c = "\nnamespace\n{\n".self::stripComments($c)."\n}\n"; + } else { + $c = self::fixNamespaceDeclarations('prefix = $prefix; + $this->classFinder = $classFinder; + } + + + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + + return true; + } + } + + + public function findFile($class) + { + if (false === $file = apc_fetch($this->prefix.$class)) { + apc_store($this->prefix.$class, $file = $this->classFinder->findFile($class)); + } + + return $file; + } +} +} + + + + +namespace Symfony\Component\HttpKernel\Bundle +{ + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; + + +interface BundleInterface extends ContainerAwareInterface +{ + + function boot(); + + + function shutdown(); + + + function build(ContainerBuilder $container); + + + function getContainerExtension(); + + + function getParent(); + + + function getName(); + + + function getNamespace(); + + + function getPath(); +} +} + + + + +namespace Symfony\Component\HttpKernel\Bundle +{ + +use Symfony\Component\DependencyInjection\ContainerAware; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\Console\Application; +use Symfony\Component\Finder\Finder; + + +abstract class Bundle extends ContainerAware implements BundleInterface +{ + protected $name; + protected $reflected; + protected $extension; + + + public function boot() + { + } + + + public function shutdown() + { + } + + + public function build(ContainerBuilder $container) + { + } + + + public function getContainerExtension() + { + if (null === $this->extension) { + $basename = preg_replace('/Bundle$/', '', $this->getName()); + + $class = $this->getNamespace().'\\DependencyInjection\\'.$basename.'Extension'; + if (class_exists($class)) { + $extension = new $class(); + + $expectedAlias = Container::underscore($basename); + if ($expectedAlias != $extension->getAlias()) { + throw new \LogicException(sprintf( + 'The extension alias for the default extension of a '. + 'bundle must be the underscored version of the '. + 'bundle name ("%s" instead of "%s")', + $expectedAlias, $extension->getAlias() + )); + } + + $this->extension = $extension; + } else { + $this->extension = false; + } + } + + if ($this->extension) { + return $this->extension; + } + } + + + public function getNamespace() + { + if (null === $this->reflected) { + $this->reflected = new \ReflectionObject($this); + } + + return $this->reflected->getNamespaceName(); + } + + + public function getPath() + { + if (null === $this->reflected) { + $this->reflected = new \ReflectionObject($this); + } + + return dirname($this->reflected->getFileName()); + } + + + public function getParent() + { + return null; + } + + + final public function getName() + { + if (null !== $this->name) { + return $this->name; + } + + $name = get_class($this); + $pos = strrpos($name, '\\'); + + return $this->name = false === $pos ? $name : substr($name, $pos + 1); + } + + + public function registerCommands(Application $application) + { + if (!$dir = realpath($this->getPath().'/Command')) { + return; + } + + $finder = new Finder(); + $finder->files()->name('*Command.php')->in($dir); + + $prefix = $this->getNamespace().'\\Command'; + foreach ($finder as $file) { + $ns = $prefix; + if ($relativePath = $file->getRelativePath()) { + $ns .= '\\'.strtr($relativePath, '/', '\\'); + } + $r = new \ReflectionClass($ns.'\\'.$file->getBasename('.php')); + if ($r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command') && !$r->isAbstract()) { + $application->add($r->newInstance()); + } + } + } +} +} + + + + +namespace Symfony\Component\Config +{ + + +class ConfigCache +{ + private $debug; + private $file; + + + public function __construct($file, $debug) + { + $this->file = $file; + $this->debug = (Boolean) $debug; + } + + + public function __toString() + { + return $this->file; + } + + + public function isFresh() + { + if (!is_file($this->file)) { + return false; + } + + if (!$this->debug) { + return true; + } + + $metadata = $this->file.'.meta'; + if (!is_file($metadata)) { + return false; + } + + $time = filemtime($this->file); + $meta = unserialize(file_get_contents($metadata)); + foreach ($meta as $resource) { + if (!$resource->isFresh($time)) { + return false; + } + } + + return true; + } + + + public function write($content, array $metadata = null) + { + $dir = dirname($this->file); + if (!is_dir($dir)) { + if (false === @mkdir($dir, 0777, true)) { + throw new \RuntimeException(sprintf('Unable to create the %s directory', $dir)); + } + } elseif (!is_writable($dir)) { + throw new \RuntimeException(sprintf('Unable to write in the %s directory', $dir)); + } + + $tmpFile = tempnam(dirname($this->file), basename($this->file)); + if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $this->file)) { + @chmod($this->file, 0666 & ~umask()); + } else { + throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $this->file)); + } + + if (null !== $metadata && true === $this->debug) { + $file = $this->file.'.meta'; + $tmpFile = tempnam(dirname($file), basename($file)); + if (false !== @file_put_contents($tmpFile, serialize($metadata)) && @rename($tmpFile, $file)) { + @chmod($file, 0666 & ~umask()); + } + } + } +} +} + + +namespace { return $loader; } + \ No newline at end of file diff --git a/app/cache/.gitkeep b/app/cache/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/check.php b/app/check.php new file mode 100644 index 0000000..daa6d0a --- /dev/null +++ b/app/check.php @@ -0,0 +1,55 @@ +getPhpIniConfigPath(); + +echo "********************************\n"; +echo "* *\n"; +echo "* Symfony requirements check *\n"; +echo "* *\n"; +echo "********************************\n\n"; + +echo $iniPath ? sprintf("* Configuration file used by PHP: %s\n\n", $iniPath) : "* WARNING: No configuration file (php.ini) used by PHP!\n\n"; + +echo "** ATTENTION **\n"; +echo "* The PHP CLI can use a different php.ini file\n"; +echo "* than the one used with your web server.\n"; +if ('\\' == DIRECTORY_SEPARATOR) { + echo "* (especially on the Windows platform)\n"; +} +echo "* To be on the safe side, please also launch the requirements check\n"; +echo "* from your web server using the web/config.php script.\n"; + +echo_title('Mandatory requirements'); + +foreach ($symfonyRequirements->getRequirements() as $req) { + echo_requirement($req); +} + +echo_title('Optional recommendations'); + +foreach ($symfonyRequirements->getRecommendations() as $req) { + echo_requirement($req); +} + +/** + * Prints a Requirement instance + */ +function echo_requirement(Requirement $requirement) +{ + $result = $requirement->isFulfilled() ? 'OK' : ($requirement->isOptional() ? 'WARNING' : 'ERROR'); + echo ' ' . str_pad($result, 9); + echo $requirement->getTestMessage() . "\n"; + + if (!$requirement->isFulfilled()) { + echo sprintf(" %s\n\n", $requirement->getHelpText()); + } +} + +function echo_title($title) +{ + echo "\n** $title **\n\n"; +} diff --git a/app/config/config.yml b/app/config/config.yml new file mode 100644 index 0000000..ff16149 --- /dev/null +++ b/app/config/config.yml @@ -0,0 +1,60 @@ +imports: + - { resource: parameters.yml } + - { resource: security.yml } + +framework: + #esi: ~ + #translator: { fallback: %locale% } + secret: %secret% + charset: UTF-8 + router: + resource: "%kernel.root_dir%/config/routing.yml" + strict_parameters: %kernel.debug% + form: true + csrf_protection: true + validation: { enable_annotations: true } + templating: { engines: ['twig'] } #assets_version: SomeVersionScheme + default_locale: %locale% + session: + auto_start: true + +# Twig Configuration +twig: + debug: %kernel.debug% + strict_variables: %kernel.debug% + +# Assetic Configuration +assetic: + debug: %kernel.debug% + use_controller: false + bundles: [ ] + #java: /usr/bin/java + filters: + cssrewrite: ~ + #closure: + # jar: %kernel.root_dir%/Resources/java/compiler.jar + #yui_css: + # jar: %kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar + +# Doctrine Configuration +doctrine: + dbal: + driver: %database_driver% + host: %database_host% + port: %database_port% + dbname: %database_name% + user: %database_user% + password: %database_password% + charset: UTF8 + + orm: + auto_generate_proxy_classes: %kernel.debug% + auto_mapping: true + +# Swiftmailer Configuration +swiftmailer: + transport: %mailer_transport% + host: %mailer_host% + username: %mailer_user% + password: %mailer_password% + spool: { type: memory } diff --git a/app/config/config_dev.yml b/app/config/config_dev.yml new file mode 100644 index 0000000..b7e675d --- /dev/null +++ b/app/config/config_dev.yml @@ -0,0 +1,26 @@ +imports: + - { resource: config.yml } + +framework: + router: { resource: "%kernel.root_dir%/config/routing_dev.yml" } + profiler: { only_exceptions: false } + +web_profiler: + toolbar: true + intercept_redirects: false + +monolog: + handlers: + main: + type: stream + path: %kernel.logs_dir%/%kernel.environment%.log + level: debug + firephp: + type: firephp + level: info + +assetic: + use_controller: true + +#swiftmailer: +# delivery_address: me@example.com diff --git a/app/config/config_prod.yml b/app/config/config_prod.yml new file mode 100644 index 0000000..0b91d4d --- /dev/null +++ b/app/config/config_prod.yml @@ -0,0 +1,19 @@ +imports: + - { resource: config.yml } + +#doctrine: +# orm: +# metadata_cache_driver: apc +# result_cache_driver: apc +# query_cache_driver: apc + +monolog: + handlers: + main: + type: fingers_crossed + action_level: error + handler: nested + nested: + type: stream + path: %kernel.logs_dir%/%kernel.environment%.log + level: debug diff --git a/app/config/config_test.yml b/app/config/config_test.yml new file mode 100644 index 0000000..e7f44e5 --- /dev/null +++ b/app/config/config_test.yml @@ -0,0 +1,14 @@ +imports: + - { resource: config_dev.yml } + +framework: + test: ~ + session: + storage_id: session.storage.mock_file + +web_profiler: + toolbar: false + intercept_redirects: false + +swiftmailer: + disable_delivery: true diff --git a/app/config/parameters.yml b/app/config/parameters.yml new file mode 100644 index 0000000..6f0c6ee --- /dev/null +++ b/app/config/parameters.yml @@ -0,0 +1,15 @@ +parameters: + database_driver: pdo_mysql + database_host: localhost + database_port: ~ + database_name: symfony + database_user: root + database_password: ~ + + mailer_transport: smtp + mailer_host: localhost + mailer_user: ~ + mailer_password: ~ + + locale: en + secret: ThisTokenIsNotSoSecretChangeIt diff --git a/app/config/routing.yml b/app/config/routing.yml new file mode 100644 index 0000000..684d364 --- /dev/null +++ b/app/config/routing.yml @@ -0,0 +1,4 @@ +# Internal routing configuration to handle ESI +#_internal: +# resource: "@FrameworkBundle/Resources/config/routing/internal.xml" +# prefix: /_internal diff --git a/app/config/routing_dev.yml b/app/config/routing_dev.yml new file mode 100644 index 0000000..22a48ae --- /dev/null +++ b/app/config/routing_dev.yml @@ -0,0 +1,27 @@ +_welcome: + pattern: / + defaults: { _controller: AcmeDemoBundle:Welcome:index } + +_demo_secured: + resource: "@AcmeDemoBundle/Controller/SecuredController.php" + type: annotation + +_demo: + resource: "@AcmeDemoBundle/Controller/DemoController.php" + type: annotation + prefix: /demo + +_wdt: + resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" + prefix: /_wdt + +_profiler: + resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" + prefix: /_profiler + +_configurator: + resource: "@SensioDistributionBundle/Resources/config/routing/webconfigurator.xml" + prefix: /_configurator + +_main: + resource: routing.yml diff --git a/app/config/security.yml b/app/config/security.yml new file mode 100644 index 0000000..e01c1c2 --- /dev/null +++ b/app/config/security.yml @@ -0,0 +1,43 @@ +jms_security_extra: + secure_all_services: false + expressions: true + +security: + encoders: + Symfony\Component\Security\Core\User\User: plaintext + + role_hierarchy: + ROLE_ADMIN: ROLE_USER + ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] + + providers: + in_memory: + memory: + users: + user: { password: userpass, roles: [ 'ROLE_USER' ] } + admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] } + + firewalls: + dev: + pattern: ^/(_(profiler|wdt)|css|images|js)/ + security: false + + login: + pattern: ^/demo/secured/login$ + security: false + + secured_area: + pattern: ^/demo/secured/ + form_login: + check_path: /demo/secured/login_check + login_path: /demo/secured/login + logout: + path: /demo/secured/logout + target: /demo/ + #anonymous: ~ + #http_basic: + # realm: "Secured Demo Area" + + access_control: + #- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https } + #- { path: ^/_internal/secure, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 } diff --git a/app/console b/app/console new file mode 100755 index 0000000..1de6db4 --- /dev/null +++ b/app/console @@ -0,0 +1,22 @@ +#!/usr/bin/env php +getParameterOption(array('--env', '-e'), getenv('SYMFONY_ENV') ?: 'dev'); +$debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(array('--no-debug', '')) && $env !== 'prod'; + +$kernel = new AppKernel($env, $debug); +$application = new Application($kernel); +$application->run($input); diff --git a/app/logs/.gitkeep b/app/logs/.gitkeep new file mode 100755 index 0000000..e69de29 diff --git a/app/phpunit.xml.dist b/app/phpunit.xml.dist new file mode 100644 index 0000000..1e31086 --- /dev/null +++ b/app/phpunit.xml.dist @@ -0,0 +1,41 @@ + + + + + + + + ../src/*/*Bundle/Tests + ../src/*/Bundle/*Bundle/Tests + + + + + + + + ../src + + ../src/*/*Bundle/Resources + ../src/*/*Bundle/Tests + ../src/*/Bundle/*Bundle/Resources + ../src/*/Bundle/*Bundle/Tests + + + + + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..e101250 --- /dev/null +++ b/composer.json @@ -0,0 +1,41 @@ +{ + "name": "symfony/framework-standard-edition", + "description": "The \"Symfony Standard Edition\" distribution", + "autoload": { + "psr-0": { "": "src/" } + }, + "require": { + "php": ">=5.3.3", + "symfony/symfony": "2.1.*", + "doctrine/orm": "2.2.*", + "doctrine/doctrine-bundle": "dev-master", + "twig/extensions": "dev-master", + "symfony/assetic-bundle": "dev-master", + "symfony/swiftmailer-bundle": "dev-master", + "symfony/monolog-bundle": "dev-master", + "sensio/distribution-bundle": "dev-master", + "sensio/framework-extra-bundle": "dev-master", + "sensio/generator-bundle": "dev-master", + "jms/security-extra-bundle": "1.1.*", + "jms/di-extra-bundle": "1.0.*" + }, + "scripts": { + "post-install-cmd": [ + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets" + ], + "post-update-cmd": [ + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets" + ] + }, + "config": { + "bin-dir": "bin" + }, + "extra": { + "symfony-app-dir": "app", + "symfony-web-dir": "web" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..3bb6230 --- /dev/null +++ b/composer.lock @@ -0,0 +1,144 @@ +{ + "hash": "60f09c83d9f0891b51ac51941970dcd5", + "packages": [ + { + "package": "doctrine/common", + "version": "2.2.2" + }, + { + "package": "doctrine/dbal", + "version": "2.2.x-dev", + "source-reference": "8cc129aa64a8de6447056bce20f0a274fe2a340b" + }, + { + "package": "doctrine/doctrine-bundle", + "version": "dev-master", + "source-reference": "94951737d6c692500d6b13bb92871935568a4ba5" + }, + { + "package": "doctrine/orm", + "version": "2.2.x-dev", + "source-reference": "5f66c65c9a8d984899903b54215d0249a45b92d6" + }, + { + "package": "jms/aop-bundle", + "version": "1.0.0" + }, + { + "package": "jms/cg", + "version": "1.0.0" + }, + { + "package": "jms/di-extra-bundle", + "version": "1.0.1" + }, + { + "package": "jms/metadata", + "version": "1.1.1" + }, + { + "package": "jms/security-extra-bundle", + "version": "1.1.0" + }, + { + "package": "kriswallsmith/assetic", + "version": "dev-master", + "alias-pretty-version": "1.1.x-dev", + "alias-version": "1.1.9999999.9999999-dev" + }, + { + "package": "kriswallsmith/assetic", + "version": "dev-master", + "source-reference": "d6f89a3170c5280ad554347dc113eb25fdf00ad7" + }, + { + "package": "monolog/monolog", + "version": "1.1.0" + }, + { + "package": "sensio/distribution-bundle", + "version": "dev-master", + "source-reference": "a360ad61fe34206a7295c1ef00b5455b2b2e1071" + }, + { + "package": "sensio/framework-extra-bundle", + "version": "dev-master", + "source-reference": "62e41b85947034b0f1dfe31bb8e76920e1488571" + }, + { + "package": "sensio/generator-bundle", + "version": "dev-master", + "source-reference": "43ed45c48db18e4a0e48aec0c098f42e56e22d36" + }, + { + "package": "swiftmailer/swiftmailer", + "version": "dev-master", + "alias-pretty-version": "4.1.x-dev", + "alias-version": "4.1.9999999.9999999-dev" + }, + { + "package": "swiftmailer/swiftmailer", + "version": "dev-master", + "source-reference": "d57ffdeed664d6061cef0047e1f5d3fc3ee3fb99" + }, + { + "package": "symfony/assetic-bundle", + "version": "dev-master", + "source-reference": "8fe7b898b08103c1d6fce64c3e279a7afd61adfc" + }, + { + "package": "symfony/monolog-bundle", + "version": "dev-master", + "source-reference": "0b5046d3d1ef93cd59ceabfa37de2665f5118c7f" + }, + { + "package": "symfony/swiftmailer-bundle", + "version": "dev-master", + "source-reference": "d05c9c514a631ee688c53c4cc5505da757bd50d3" + }, + { + "package": "symfony/symfony", + "version": "dev-master", + "alias-pretty-version": "2.1.x-dev", + "alias-version": "2.1.9999999.9999999-dev" + }, + { + "package": "symfony/symfony", + "version": "dev-master", + "source-reference": "v2.1.0-BETA1" + }, + { + "package": "twig/extensions", + "version": "dev-master", + "source-reference": "feb6d3f10c411e2631997c0a905aa581c80305c1" + }, + { + "package": "twig/twig", + "version": "dev-master", + "alias-pretty-version": "1.8.x-dev", + "alias-version": "1.8.9999999.9999999-dev" + }, + { + "package": "twig/twig", + "version": "dev-master", + "source-reference": "4679ad51c5390648b7ea4c8f0ecd2c0c344145ba" + } + ], + "packages-dev": [ + + ], + "aliases": [ + + ], + "minimum-stability": "dev", + "stability-flags": { + "doctrine/doctrine-bundle": 20, + "twig/extensions": 20, + "symfony/assetic-bundle": 20, + "symfony/swiftmailer-bundle": 20, + "symfony/monolog-bundle": 20, + "sensio/distribution-bundle": 20, + "sensio/framework-extra-bundle": 20, + "sensio/generator-bundle": 20 + } +} diff --git a/src/.htaccess b/src/.htaccess new file mode 100644 index 0000000..3418e55 --- /dev/null +++ b/src/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/src/Acme/DemoBundle/AcmeDemoBundle.php b/src/Acme/DemoBundle/AcmeDemoBundle.php new file mode 100644 index 0000000..269fc1e --- /dev/null +++ b/src/Acme/DemoBundle/AcmeDemoBundle.php @@ -0,0 +1,9 @@ + $name); + } + + /** + * @Route("/contact", name="_demo_contact") + * @Template() + */ + public function contactAction() + { + $form = $this->get('form.factory')->create(new ContactType()); + + $request = $this->get('request'); + if ('POST' == $request->getMethod()) { + $form->bindRequest($request); + if ($form->isValid()) { + $mailer = $this->get('mailer'); + // .. setup a message and send it + // http://symfony.com/doc/current/cookbook/email.html + + $this->get('session')->setFlash('notice', 'Message sent!'); + + return new RedirectResponse($this->generateUrl('_demo')); + } + } + + return array('form' => $form->createView()); + } +} diff --git a/src/Acme/DemoBundle/Controller/SecuredController.php b/src/Acme/DemoBundle/Controller/SecuredController.php new file mode 100644 index 0000000..9848e42 --- /dev/null +++ b/src/Acme/DemoBundle/Controller/SecuredController.php @@ -0,0 +1,69 @@ +get('request')->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) { + $error = $this->get('request')->attributes->get(SecurityContext::AUTHENTICATION_ERROR); + } else { + $error = $this->get('request')->getSession()->get(SecurityContext::AUTHENTICATION_ERROR); + } + + return array( + 'last_username' => $this->get('request')->getSession()->get(SecurityContext::LAST_USERNAME), + 'error' => $error, + ); + } + + /** + * @Route("/login_check", name="_security_check") + */ + public function securityCheckAction() + { + // The security layer will intercept this request + } + + /** + * @Route("/logout", name="_demo_logout") + */ + public function logoutAction() + { + // The security layer will intercept this request + } + + /** + * @Route("/hello", defaults={"name"="World"}), + * @Route("/hello/{name}", name="_demo_secured_hello") + * @Template() + */ + public function helloAction($name) + { + return array('name' => $name); + } + + /** + * @Route("/hello/admin/{name}", name="_demo_secured_hello_admin") + * @Secure(roles="ROLE_ADMIN") + * @Template() + */ + public function helloadminAction($name) + { + return array('name' => $name); + } +} diff --git a/src/Acme/DemoBundle/Controller/WelcomeController.php b/src/Acme/DemoBundle/Controller/WelcomeController.php new file mode 100644 index 0000000..acceedf --- /dev/null +++ b/src/Acme/DemoBundle/Controller/WelcomeController.php @@ -0,0 +1,18 @@ +render('AcmeDemoBundle:Welcome:index.html.twig'); + } +} diff --git a/src/Acme/DemoBundle/DependencyInjection/AcmeDemoExtension.php b/src/Acme/DemoBundle/DependencyInjection/AcmeDemoExtension.php new file mode 100644 index 0000000..6dfcc82 --- /dev/null +++ b/src/Acme/DemoBundle/DependencyInjection/AcmeDemoExtension.php @@ -0,0 +1,22 @@ +load('services.xml'); + } + + public function getAlias() + { + return 'acme_demo'; + } +} diff --git a/src/Acme/DemoBundle/EventListener/ControllerListener.php b/src/Acme/DemoBundle/EventListener/ControllerListener.php new file mode 100644 index 0000000..5274f71 --- /dev/null +++ b/src/Acme/DemoBundle/EventListener/ControllerListener.php @@ -0,0 +1,25 @@ +extension = $extension; + } + + public function onKernelController(FilterControllerEvent $event) + { + if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) { + $this->extension->setController($event->getController()); + } + } +} diff --git a/src/Acme/DemoBundle/Form/ContactType.php b/src/Acme/DemoBundle/Form/ContactType.php new file mode 100644 index 0000000..2c76cdb --- /dev/null +++ b/src/Acme/DemoBundle/Form/ContactType.php @@ -0,0 +1,20 @@ +add('email', 'email'); + $builder->add('message', 'textarea'); + } + + public function getName() + { + return 'contact'; + } +} diff --git a/src/Acme/DemoBundle/Resources/config/services.xml b/src/Acme/DemoBundle/Resources/config/services.xml new file mode 100644 index 0000000..d6274ce --- /dev/null +++ b/src/Acme/DemoBundle/Resources/config/services.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/src/Acme/DemoBundle/Resources/public/css/demo.css b/src/Acme/DemoBundle/Resources/public/css/demo.css new file mode 100644 index 0000000..0a9d8e2 --- /dev/null +++ b/src/Acme/DemoBundle/Resources/public/css/demo.css @@ -0,0 +1,294 @@ +/* +Copyright (c) 2010, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.com/yui/license.html +version: 2.8.2r1 + +Reset +*/ + +html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;} + +html, body +{ + background-color: #EFEFEF; +} + +body +{ + font-size: 14px; + font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + color: #313131; +} + +a +{ + color: #08C; + text-decoration: none; +} + +a:hover +{ + text-decoration: underline; +} + +strong +{ + font-weight: bold; +} + +em +{ + font-style: italic; +} + +h1, h2, h3 +{ + font-family: Georgia, "Times New Roman", Times, serif; + color: #404040; +} + +h1 +{ + font-size: 45px; + padding-bottom: 30px; +} + +h2 +{ + font-weight: bold; + color: #FFFFFF; + /* Font is duplicated of body (sans-serif) */ + font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + + margin-bottom: 10px; + background-color: #aacd4e; + padding: 2px 4px; + display: inline-block; + text-transform: uppercase; + +} + +p +{ + line-height: 20px; + padding-bottom: 20px; +} + +ul#demo-list a +{ + background: url(../images/blue-arrow.png) no-repeat right 6px; + padding-right: 10px; + margin-right: 30px; +} + +ul, ol +{ + padding-left: 20px; +} + +li +{ + padding-bottom: 18px; +} + +ol li +{ + list-style-type: decimal; +} + +ul li +{ + list-style-type: none; +} + +#symfony-header +{ + position: relative; + padding: 30px 30px 20px 30px; +} + +#symfony-wrapper +{ + width: 970px; + margin: 0 auto; +} + +.symfony-content +{ + background-color: white; + border: 1px solid #DFDFDF; + padding: 50px; + -moz-border-radius: 16px; + -webkit-border-radius: 16px; + border-radius: 16px; + margin-bottom: 20px; + word-wrap: break-word; +} + +#symfony-search +{ + position: absolute; + top: 50px; + right: 30px; +} + +#symfony-search input[type="search"] +{ + -webkit-appearance: textfield; +} + +#symfony-search-field +{ + width: 190px; +} + +#symfony-search label +{ + display: block; + float: left; + width: 20px; + height: 25px; + background: url(../images/search.png) no-repeat left 5px; +} + +#symfony-search label span +{ + display: none; +} + +input[type=text], input[type=password] +{ + border: 1px solid #DADADA; + background: white url(../images/field-background.gif) repeat-x left top; + padding: 5px 6px; + color: #565656; + font-family: 'Lucida Sans Unicode', 'Lucida Grande', Verdana, Arial, Helvetica, sans-serif; + font-size: 12px; +} + +.symfony-button-grey, +.symfony-button-green +{ + font-size: 0.85em; + font-weight: bold; + + cursor: pointer; + + display: inline-block; + outline: none; + + text-align: center; + text-transform: uppercase; + + padding: 3px 10px; + + text-shadow: 0 1px 1px rgba(0,0,0,.3); + + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.symfony-button-grey +{ + color: #868686; + font-weight: normal; + + padding: 5px 10px; + border: solid 1px #d7d7d7; + background: #ffffff; + background: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#d7d7d7)); + background: -moz-linear-gradient(top, #ffffff, #d7d7d7); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#d7d7d7'); +} + +.symfony-button-green +{ + padding: 5px 12px; + + color: white; + + border: solid 1px #a7da39; + background: #a7da39; + background: -webkit-gradient(linear, left top, left bottom, from(#a7da39), to(#6a9211)); + background: -moz-linear-gradient(top, #a7da39, #6a9211); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#a7da39', endColorstr='#6a9211'); +} + +.symfony-blocks-welcome +{ + overflow: hidden; +} + +.symfony-blocks-welcome > div +{ + background-color: whitesmoke; + float: left; + width: 240px; + margin-right: 14px; + text-align: center; + padding: 26px 20px; +} + +.symfony-blocks-welcome > div.block-demo +{ + margin-right: 0; +} + +.symfony-blocks-welcome .illustration +{ + padding-bottom: 20px; +} + +.symfony-blocks-help +{ + overflow: hidden; +} + +.symfony-blocks-help +{ + margin-top: 30px; + padding: 18px; + border: 1px solid #E6E6E6; +} + +.symfony-blocks-help > div +{ + width: 254px; + float: left; +} + +.flash-message +{ + padding: 10px; + margin: 5px; + margin-top: 15px; + background-color: #ffe; +} + +.error +{ + color: red; +} + +#login label, #contact_form label +{ + display: block; + float: left; + width: 90px; +} + +ul#menu +{ + float: right; + margin-bottom: 20px; + padding-left: 0; +} + +#menu li +{ + padding-left: 0; + margin-right: 10px; + display: inline; +} diff --git a/src/Acme/DemoBundle/Resources/public/images/blue-arrow.png b/src/Acme/DemoBundle/Resources/public/images/blue-arrow.png new file mode 100644 index 0000000..fa82d4b Binary files /dev/null and b/src/Acme/DemoBundle/Resources/public/images/blue-arrow.png differ diff --git a/src/Acme/DemoBundle/Resources/public/images/field-background.gif b/src/Acme/DemoBundle/Resources/public/images/field-background.gif new file mode 100644 index 0000000..7c0efc1 Binary files /dev/null and b/src/Acme/DemoBundle/Resources/public/images/field-background.gif differ diff --git a/src/Acme/DemoBundle/Resources/public/images/logo.gif b/src/Acme/DemoBundle/Resources/public/images/logo.gif new file mode 100644 index 0000000..703cf45 Binary files /dev/null and b/src/Acme/DemoBundle/Resources/public/images/logo.gif differ diff --git a/src/Acme/DemoBundle/Resources/public/images/search.png b/src/Acme/DemoBundle/Resources/public/images/search.png new file mode 100644 index 0000000..3c88b6a Binary files /dev/null and b/src/Acme/DemoBundle/Resources/public/images/search.png differ diff --git a/src/Acme/DemoBundle/Resources/public/images/welcome-configure.gif b/src/Acme/DemoBundle/Resources/public/images/welcome-configure.gif new file mode 100644 index 0000000..931179a Binary files /dev/null and b/src/Acme/DemoBundle/Resources/public/images/welcome-configure.gif differ diff --git a/src/Acme/DemoBundle/Resources/public/images/welcome-demo.gif b/src/Acme/DemoBundle/Resources/public/images/welcome-demo.gif new file mode 100644 index 0000000..0623de5 Binary files /dev/null and b/src/Acme/DemoBundle/Resources/public/images/welcome-demo.gif differ diff --git a/src/Acme/DemoBundle/Resources/public/images/welcome-quick-tour.gif b/src/Acme/DemoBundle/Resources/public/images/welcome-quick-tour.gif new file mode 100644 index 0000000..b9018b1 Binary files /dev/null and b/src/Acme/DemoBundle/Resources/public/images/welcome-quick-tour.gif differ diff --git a/src/Acme/DemoBundle/Resources/views/Demo/contact.html.twig b/src/Acme/DemoBundle/Resources/views/Demo/contact.html.twig new file mode 100644 index 0000000..e5b7523 --- /dev/null +++ b/src/Acme/DemoBundle/Resources/views/Demo/contact.html.twig @@ -0,0 +1,15 @@ +{% extends "AcmeDemoBundle::layout.html.twig" %} + +{% block title "Symfony - Contact form" %} + +{% block content %} +
+ {{ form_errors(form) }} + + {{ form_row(form.email) }} + {{ form_row(form.message) }} + + {{ form_rest(form) }} + +
+{% endblock %} diff --git a/src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig b/src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig new file mode 100644 index 0000000..3997ff6 --- /dev/null +++ b/src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig @@ -0,0 +1,9 @@ +{% extends "AcmeDemoBundle::layout.html.twig" %} + +{% block title "Hello " ~ name %} + +{% block content %} +

Hello {{ name }}!

+{% endblock %} + +{% set code = code(_self) %} diff --git a/src/Acme/DemoBundle/Resources/views/Demo/index.html.twig b/src/Acme/DemoBundle/Resources/views/Demo/index.html.twig new file mode 100644 index 0000000..75f118d --- /dev/null +++ b/src/Acme/DemoBundle/Resources/views/Demo/index.html.twig @@ -0,0 +1,14 @@ +{% extends "AcmeDemoBundle::layout.html.twig" %} + +{% block title "Symfony - Demos" %} + +{% block content_header '' %} + +{% block content %} +

Available demos

+ +{% endblock %} diff --git a/src/Acme/DemoBundle/Resources/views/Secured/hello.html.twig b/src/Acme/DemoBundle/Resources/views/Secured/hello.html.twig new file mode 100644 index 0000000..c8da283 --- /dev/null +++ b/src/Acme/DemoBundle/Resources/views/Secured/hello.html.twig @@ -0,0 +1,11 @@ +{% extends "AcmeDemoBundle:Secured:layout.html.twig" %} + +{% block title "Hello " ~ name %} + +{% block content %} +

Hello {{ name }}!

+ + Hello resource secured for admin only. +{% endblock %} + +{% set code = code(_self) %} diff --git a/src/Acme/DemoBundle/Resources/views/Secured/helloadmin.html.twig b/src/Acme/DemoBundle/Resources/views/Secured/helloadmin.html.twig new file mode 100644 index 0000000..425213e --- /dev/null +++ b/src/Acme/DemoBundle/Resources/views/Secured/helloadmin.html.twig @@ -0,0 +1,9 @@ +{% extends "AcmeDemoBundle:Secured:layout.html.twig" %} + +{% block title "Hello " ~ name %} + +{% block content %} +

Hello {{ name }} secured for Admins only!

+{% endblock %} + +{% set code = code(_self) %} diff --git a/src/Acme/DemoBundle/Resources/views/Secured/layout.html.twig b/src/Acme/DemoBundle/Resources/views/Secured/layout.html.twig new file mode 100644 index 0000000..aeea55c --- /dev/null +++ b/src/Acme/DemoBundle/Resources/views/Secured/layout.html.twig @@ -0,0 +1,6 @@ +{% extends "AcmeDemoBundle::layout.html.twig" %} + +{% block content_header_more %} + {{ parent() }} +
  • logged in as {{ app.user ? app.user.username : 'Anonymous' }} - Logout
  • +{% endblock %} diff --git a/src/Acme/DemoBundle/Resources/views/Secured/login.html.twig b/src/Acme/DemoBundle/Resources/views/Secured/login.html.twig new file mode 100644 index 0000000..005175d --- /dev/null +++ b/src/Acme/DemoBundle/Resources/views/Secured/login.html.twig @@ -0,0 +1,29 @@ +{% extends 'AcmeDemoBundle::layout.html.twig' %} + +{% block content %} +

    Login

    + +

    + Choose between two default users: user/userpass (ROLE_USER) or admin/adminpass (ROLE_ADMIN) +

    + + {% if error %} +
    {{ error.message }}
    + {% endif %} + +
    +
    + + +
    + +
    + + +
    + + +
    +{% endblock %} + +{% set code = code(_self) %} diff --git a/src/Acme/DemoBundle/Resources/views/Welcome/index.html.twig b/src/Acme/DemoBundle/Resources/views/Welcome/index.html.twig new file mode 100644 index 0000000..cbad2ab --- /dev/null +++ b/src/Acme/DemoBundle/Resources/views/Welcome/index.html.twig @@ -0,0 +1,63 @@ +{% extends 'AcmeDemoBundle::layout.html.twig' %} + +{% block title %}Symfony - Welcome{% endblock %} + +{% block content_header '' %} + +{% block content %} +

    Welcome!

    + +

    Congratulations! You have successfully installed a new Symfony application.

    + +
    + + {% if app.environment == 'dev' %} +
    +
    + Configure your application +
    + Configure +
    + {% endif %} +
    +
    + Demo +
    + Run The Demo +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +{% endblock %} diff --git a/src/Acme/DemoBundle/Resources/views/layout.html.twig b/src/Acme/DemoBundle/Resources/views/layout.html.twig new file mode 100644 index 0000000..3dd3936 --- /dev/null +++ b/src/Acme/DemoBundle/Resources/views/layout.html.twig @@ -0,0 +1,49 @@ + + + + + {% block title %}Demo Bundle{% endblock %} + + + + +
    +
    + + Symfony logo + + +
    + + {% for flashMessage in app.session.flashbag.get('notice') %} +
    + Notice: {{ flashMessage }} +
    + {% endfor %} + + {% block content_header %} + + +
    + {% endblock %} + +
    + {% block content %} + {% endblock %} +
    + + {% if code is defined %} +

    Code behind this page

    +
    {{ code|raw }}
    + {% endif %} +
    + + diff --git a/src/Acme/DemoBundle/Tests/Controller/DemoControllerTest.php b/src/Acme/DemoBundle/Tests/Controller/DemoControllerTest.php new file mode 100644 index 0000000..2941af9 --- /dev/null +++ b/src/Acme/DemoBundle/Tests/Controller/DemoControllerTest.php @@ -0,0 +1,17 @@ +request('GET', '/demo/hello/Fabien'); + + $this->assertTrue($crawler->filter('html:contains("Hello Fabien")')->count() > 0); + } +} diff --git a/src/Acme/DemoBundle/Twig/Extension/DemoExtension.php b/src/Acme/DemoBundle/Twig/Extension/DemoExtension.php new file mode 100644 index 0000000..8f6533a --- /dev/null +++ b/src/Acme/DemoBundle/Twig/Extension/DemoExtension.php @@ -0,0 +1,80 @@ +loader = $loader; + } + + public function setController($controller) + { + $this->controller = $controller; + } + + /** + * {@inheritdoc} + */ + public function getFunctions() + { + return array( + 'code' => new \Twig_Function_Method($this, 'getCode', array('is_safe' => array('html'))), + ); + } + + public function getCode($template) + { + $controller = htmlspecialchars($this->getControllerCode(), ENT_QUOTES, 'UTF-8'); + $template = htmlspecialchars($this->getTemplateCode($template), ENT_QUOTES, 'UTF-8'); + + // remove the code block + $template = str_replace('{% set code = code(_self) %}', '', $template); + + return <<Controller Code

    +
    $controller
    + +

    Template Code

    +
    $template
    +EOF; + } + + protected function getControllerCode() + { + $class = get_class($this->controller[0]); + if (class_exists('CG\Core\ClassUtils')) { + $class = ClassUtils::getUserClass($class); + } + + $r = new \ReflectionClass($class); + $m = $r->getMethod($this->controller[1]); + + $code = file($r->getFilename()); + + return ' '.$m->getDocComment()."\n".implode('', array_slice($code, $m->getStartline() - 1, $m->getEndLine() - $m->getStartline() + 1)); + } + + protected function getTemplateCode($template) + { + return $this->loader->getSource($template->getTemplateName()); + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'demo'; + } +} diff --git a/vendor/ClassLoader.php b/vendor/ClassLoader.php new file mode 100644 index 0000000..d07cb31 --- /dev/null +++ b/vendor/ClassLoader.php @@ -0,0 +1,203 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements an PSR-0 class loader + * + * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + * + * $loader = new ComposerClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + */ +class ClassLoader +{ + private $prefixes = array(); + private $fallbackDirs = array(); + private $useIncludePath = false; + private $classMap = array(); + + public function getPrefixes() + { + return $this->prefixes; + } + + public function getFallbackDirs() + { + return $this->fallbackDirs; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of classes + * + * @param string $prefix The classes prefix + * @param array|string $paths The location(s) of the classes + */ + public function add($prefix, $paths) + { + if (!$prefix) { + foreach ((array) $paths as $path) { + $this->fallbackDirs[] = $path; + } + return; + } + if (isset($this->prefixes[$prefix])) { + $this->prefixes[$prefix] = array_merge( + $this->prefixes[$prefix], + (array) $paths + ); + } else { + $this->prefixes[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include for class files. + * + * @param Boolean $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return Boolean + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Registers this instance as an autoloader. + * + * @param Boolean $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return Boolean|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|null The path, if found + */ + public function findFile($class) + { + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR; + $className = substr($class, $pos + 1); + } else { + // PEAR-like class name + $classPath = null; + $className = $class; + } + + $classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; + + foreach ($this->prefixes as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) { + return $dir . DIRECTORY_SEPARATOR . $classPath; + } + } + } + } + + foreach ($this->fallbackDirs as $dir) { + if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) { + return $dir . DIRECTORY_SEPARATOR . $classPath; + } + } + + if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) { + return $file; + } + } +} diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 0000000..d979957 --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,27 @@ + $path) { + $loader->add($namespace, $path); + } + + $classMap = require $composerDir . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + + $loader->register(); + + return $loader; +}); diff --git a/vendor/autoload_classmap.php b/vendor/autoload_classmap.php new file mode 100644 index 0000000..b382d27 --- /dev/null +++ b/vendor/autoload_classmap.php @@ -0,0 +1,9 @@ + $vendorDir . '/twig/extensions/lib/', + 'Twig_' => $vendorDir . '/twig/twig/lib/', + 'Symfony\\Bundle\\SwiftmailerBundle' => $vendorDir . '/symfony/swiftmailer-bundle/', + 'Symfony\\Bundle\\MonologBundle' => $vendorDir . '/symfony/monolog-bundle/', + 'Symfony\\Bundle\\AsseticBundle' => $vendorDir . '/symfony/assetic-bundle/', + 'Symfony\\Bridge\\Swiftmailer' => $vendorDir . '/symfony/swiftmailer-bridge/', + 'Symfony' => $vendorDir . '/symfony/symfony/src/', + 'SessionHandlerInterface' => $vendorDir . '/symfony/symfony/src/Symfony/Component/HttpFoundation/Resources/stubs', + 'Sensio\\Bundle\\GeneratorBundle' => $vendorDir . '/sensio/generator-bundle/', + 'Sensio\\Bundle\\FrameworkExtraBundle' => $vendorDir . '/sensio/framework-extra-bundle/', + 'Sensio\\Bundle\\DistributionBundle' => $vendorDir . '/sensio/distribution-bundle/', + 'Monolog' => $vendorDir . '/monolog/monolog/src/', + 'Metadata\\' => $vendorDir . '/jms/metadata/src/', + 'JMS\\SecurityExtraBundle' => $vendorDir . '/jms/security-extra-bundle/', + 'JMS\\DiExtraBundle' => $vendorDir . '/jms/di-extra-bundle/', + 'JMS\\AopBundle' => $vendorDir . '/jms/aop-bundle/', + 'Doctrine\\ORM' => $vendorDir . '/doctrine/orm/lib/', + 'Doctrine\\DBAL' => $vendorDir . '/doctrine/dbal/lib/', + 'Doctrine\\Common' => $vendorDir . '/doctrine/common/lib/', + 'Doctrine\\Bundle\\DoctrineBundle' => $vendorDir . '/doctrine/doctrine-bundle/', + 'CG\\' => $vendorDir . '/jms/cg/src/', + 'Assetic' => $vendorDir . '/kriswallsmith/assetic/src/', + '' => $baseDir . '/src/', +); diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php new file mode 100644 index 0000000..5b9574e --- /dev/null +++ b/vendor/composer/ClassLoader.php @@ -0,0 +1,205 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0 class loader + * + * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + */ +class ClassLoader +{ + private $prefixes = array(); + private $fallbackDirs = array(); + private $useIncludePath = false; + private $classMap = array(); + + public function getPrefixes() + { + return $this->prefixes; + } + + public function getFallbackDirs() + { + return $this->fallbackDirs; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of classes + * + * @param string $prefix The classes prefix + * @param array|string $paths The location(s) of the classes + */ + public function add($prefix, $paths) + { + if (!$prefix) { + foreach ((array) $paths as $path) { + $this->fallbackDirs[] = $path; + } + + return; + } + if (isset($this->prefixes[$prefix])) { + $this->prefixes[$prefix] = array_merge( + $this->prefixes[$prefix], + (array) $paths + ); + } else { + $this->prefixes[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param Boolean $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return Boolean + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Registers this instance as an autoloader. + * + * @param Boolean $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return Boolean|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + include $file; + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|null The path, if found + */ + public function findFile($class) + { + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR; + $className = substr($class, $pos + 1); + } else { + // PEAR-like class name + $classPath = null; + $className = $class; + } + + $classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; + + foreach ($this->prefixes as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) { + return $dir . DIRECTORY_SEPARATOR . $classPath; + } + } + } + } + + foreach ($this->fallbackDirs as $dir) { + if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) { + return $dir . DIRECTORY_SEPARATOR . $classPath; + } + } + + if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) { + return $file; + } + } +} diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..4a9177d --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,9 @@ + $vendorDir . '/twig/extensions/lib/', + 'Twig_' => $vendorDir . '/twig/twig/lib/', + 'Symfony\\Bundle\\SwiftmailerBundle' => $vendorDir . '/symfony/swiftmailer-bundle/', + 'Symfony\\Bundle\\MonologBundle' => $vendorDir . '/symfony/monolog-bundle/', + 'Symfony\\Bundle\\AsseticBundle' => $vendorDir . '/symfony/assetic-bundle/', + 'Symfony' => $vendorDir . '/symfony/symfony/src/', + 'SessionHandlerInterface' => $vendorDir . '/symfony/symfony/src/Symfony/Component/HttpFoundation/Resources/stubs', + 'Sensio\\Bundle\\GeneratorBundle' => $vendorDir . '/sensio/generator-bundle/', + 'Sensio\\Bundle\\FrameworkExtraBundle' => $vendorDir . '/sensio/framework-extra-bundle/', + 'Sensio\\Bundle\\DistributionBundle' => $vendorDir . '/sensio/distribution-bundle/', + 'Monolog' => $vendorDir . '/monolog/monolog/src/', + 'Metadata\\' => $vendorDir . '/jms/metadata/src/', + 'JMS\\SecurityExtraBundle' => $vendorDir . '/jms/security-extra-bundle/', + 'JMS\\DiExtraBundle' => $vendorDir . '/jms/di-extra-bundle/', + 'JMS\\AopBundle' => $vendorDir . '/jms/aop-bundle/', + 'Doctrine\\ORM' => $vendorDir . '/doctrine/orm/lib/', + 'Doctrine\\DBAL' => $vendorDir . '/doctrine/dbal/lib/', + 'Doctrine\\Common' => $vendorDir . '/doctrine/common/lib/', + 'Doctrine\\Bundle\\DoctrineBundle' => $vendorDir . '/doctrine/doctrine-bundle/', + 'CG\\' => $vendorDir . '/jms/cg/src/', + 'Assetic' => $vendorDir . '/kriswallsmith/assetic/src/', + '' => $baseDir . '/src/', +); diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json new file mode 100644 index 0000000..51ab6ef --- /dev/null +++ b/vendor/composer/installed.json @@ -0,0 +1,1171 @@ +[ + { + "name": "jms/metadata", + "version": "1.1.1", + "version_normalized": "1.1.1.0", + "time": "2011-12-31 21:32:49", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/metadata", + "reference": "1.1.1" + }, + "dist": { + "type": "zip", + "url": "https://github.com/schmittjoh/metadata/zipball/1.1.1", + "reference": "1.1.1", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "installation-source": "dist", + "license": [ + "Apache" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + } + ], + "description": "Class/method/property metadata management in PHP", + "keywords": [ + "annotations", + "yaml", + "xml", + "metadata" + ], + "autoload": { + "psr-0": { + "Metadata\\": "src/" + } + } + }, + { + "name": "jms/cg", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "time": "2011-12-31 20:40:52", + "source": { + "type": "git", + "url": "git://github.com/schmittjoh/cg-library.git", + "reference": "1.0.0" + }, + "dist": { + "type": "zip", + "url": "https://github.com/schmittjoh/cg-library/zipball/1.0.0", + "reference": "1.0.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "installation-source": "dist", + "license": [ + "Apache" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + } + ], + "description": "Toolset for generating PHP code", + "keywords": [ + "code generation" + ], + "autoload": { + "psr-0": { + "CG\\": "src/" + } + } + }, + { + "name": "jms/aop-bundle", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "target-dir": "JMS/AopBundle", + "time": "2011-12-31 20:50:26", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/JMSAopBundle", + "reference": "1.0.0" + }, + "dist": { + "type": "zip", + "url": "https://github.com/schmittjoh/JMSAopBundle/zipball/1.0.0", + "reference": "1.0.0", + "shasum": "" + }, + "require": { + "symfony/framework-bundle": "2.*", + "jms/cg": "1.0.0" + }, + "type": "symfony-bundle", + "installation-source": "dist", + "license": [ + "Apache" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + } + ], + "description": "Adds AOP capabilities to Symfony2", + "keywords": [ + "annotations", + "aop" + ], + "autoload": { + "psr-0": { + "JMS\\AopBundle": "" + } + } + }, + { + "name": "jms/security-extra-bundle", + "version": "1.1.0", + "version_normalized": "1.1.0.0", + "target-dir": "JMS/SecurityExtraBundle", + "time": "2012-01-01 00:38:12", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/JMSSecurityExtraBundle", + "reference": "1.1.0" + }, + "dist": { + "type": "zip", + "url": "https://github.com/schmittjoh/JMSSecurityExtraBundle/zipball/1.1.0", + "reference": "1.1.0", + "shasum": "" + }, + "require": { + "symfony/framework-bundle": "2.*", + "jms/metadata": "1.1.*", + "jms/aop-bundle": "1.0.*" + }, + "type": "symfony-bundle", + "installation-source": "source", + "license": [ + "Apache" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + } + ], + "description": "Enhances the Symfony2 Security Component by adding several new features", + "keywords": [ + "annotations", + "authorization" + ], + "autoload": { + "psr-0": { + "JMS\\SecurityExtraBundle": "" + } + } + }, + { + "name": "jms/di-extra-bundle", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "target-dir": "JMS/DiExtraBundle", + "time": "2012-02-26 16:01:54", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/JMSDiExtraBundle", + "reference": "1.0.1" + }, + "dist": { + "type": "zip", + "url": "https://github.com/schmittjoh/JMSDiExtraBundle/zipball/1.0.1", + "reference": "1.0.1", + "shasum": "" + }, + "require": { + "symfony/framework-bundle": "2.*", + "jms/metadata": "1.1.*" + }, + "type": "symfony-bundle", + "installation-source": "dist", + "license": [ + "Apache" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + } + ], + "description": "Allows to configure dependency injection using annotations", + "keywords": [ + "dependency injection", + "annotations" + ], + "autoload": { + "psr-0": { + "JMS\\DiExtraBundle": "" + } + } + }, + { + "name": "doctrine/common", + "version": "2.2.2", + "version_normalized": "2.2.2.0", + "time": "2012-04-11 01:46:44", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common", + "reference": "2.2.2" + }, + "dist": { + "type": "zip", + "url": "https://github.com/doctrine/common/zipball/2.2.2", + "reference": "2.2.2", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "installation-source": "dist", + "license": [ + "LGPL" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": null + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org", + "homepage": null + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de", + "homepage": null + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "collections", + "spl", + "eventmanager", + "annotations", + "persistence" + ], + "autoload": { + "psr-0": { + "Doctrine\\Common": "lib/" + } + } + }, + { + "name": "monolog/monolog", + "version": "1.1.0", + "version_normalized": "1.1.0.0", + "time": "2012-04-21 12:27:40", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "1.1.0" + }, + "dist": { + "type": "zip", + "url": "https://github.com/Seldaek/monolog/zipball/1.1.0", + "reference": "1.1.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "mlehner/gelf-php": "1.0.*" + }, + "suggest": { + "mlehner/gelf-php": "Allow sending log messages to a GrayLog2 server" + }, + "type": "library", + "installation-source": "dist", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be", + "role": null + } + ], + "description": "Logging for PHP 5.3", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging" + ], + "autoload": { + "psr-0": { + "Monolog": "src/" + } + } + }, + { + "name": "twig/extensions", + "version": "dev-master", + "version_normalized": "9999999-dev", + "time": "2012-05-19 19:28:19", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Twig-extensions", + "reference": "feb6d3f10c411e2631997c0a905aa581c80305c1" + }, + "dist": { + "type": "zip", + "url": "https://github.com/fabpot/Twig-extensions/zipball/feb6d3f10c411e2631997c0a905aa581c80305c1", + "reference": "feb6d3f10c411e2631997c0a905aa581c80305c1", + "shasum": "" + }, + "require": { + "twig/twig": "1.*" + }, + "type": "library", + "installation-source": "source", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": null, + "role": null + } + ], + "description": "Common additional features for Twig that do not directly belong in core", + "homepage": "https://github.com/fabpot/Twig-extensions", + "keywords": [ + "debug", + "i18n", + "text" + ], + "autoload": { + "psr-0": { + "Twig_Extensions_": "lib/" + } + } + }, + { + "name": "symfony/monolog-bundle", + "version": "dev-master", + "version_normalized": "9999999-dev", + "target-dir": "Symfony/Bundle/MonologBundle", + "time": "2012-06-13 05:14:37", + "source": { + "type": "git", + "url": "https://github.com/symfony/MonologBundle", + "reference": "0b5046d3d1ef93cd59ceabfa37de2665f5118c7f" + }, + "dist": { + "type": "zip", + "url": "https://github.com/symfony/MonologBundle/zipball/0b5046d3d1ef93cd59ceabfa37de2665f5118c7f", + "reference": "0b5046d3d1ef93cd59ceabfa37de2665f5118c7f", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "monolog/monolog": "1.*", + "symfony/monolog-bridge": "2.1.*", + "symfony/dependency-injection": "2.1.*", + "symfony/config": "2.1.*" + }, + "require-dev": { + "symfony/yaml": "2.1.*" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "installation-source": "source", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": null, + "role": null + }, + { + "name": "Symfony Community", + "email": null, + "homepage": "http://symfony.com/contributors", + "role": null + } + ], + "description": "Symfony MonologBundle", + "homepage": "http://symfony.com", + "autoload": { + "psr-0": { + "Symfony\\Bundle\\MonologBundle": "" + } + } + }, + { + "name": "kriswallsmith/assetic", + "version": "dev-master", + "version_normalized": "9999999-dev", + "time": "2012-06-10 23:41:54", + "source": { + "type": "git", + "url": "http://github.com/kriswallsmith/assetic.git", + "reference": "d6f89a3170c5280ad554347dc113eb25fdf00ad7" + }, + "dist": { + "type": "zip", + "url": "https://github.com/kriswallsmith/assetic/zipball/d6f89a3170c5280ad554347dc113eb25fdf00ad7", + "reference": "d6f89a3170c5280ad554347dc113eb25fdf00ad7", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "symfony/process": "2.1.*" + }, + "require-dev": { + "twig/twig": ">=1.6.0,<2.0", + "leafo/lessphp": "*" + }, + "suggest": { + "twig/twig": "Assetic provides the integration with the Twig templating engine", + "leafo/lessphp": "Assetic provides the integration with the lessphp LESS compiler" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "installation-source": "source", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/", + "role": null + } + ], + "description": "Asset Management for PHP", + "homepage": "https://github.com/kriswallsmith/assetic", + "keywords": [ + "assets", + "compression", + "minification" + ], + "autoload": { + "psr-0": { + "Assetic": "src/" + } + } + }, + { + "name": "doctrine/orm", + "version": "2.2.x-dev", + "version_normalized": "2.2.9999999.9999999-dev", + "time": "2012-05-26 01:13:46", + "source": { + "type": "git", + "url": "git://github.com/doctrine/doctrine2.git", + "reference": "5f66c65c9a8d984899903b54215d0249a45b92d6" + }, + "dist": { + "type": "zip", + "url": "https://github.com/doctrine/doctrine2/zipball/5f66c65c9a8d984899903b54215d0249a45b92d6", + "reference": "5f66c65c9a8d984899903b54215d0249a45b92d6", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "ext-pdo": "*", + "doctrine/common": "2.2.*", + "doctrine/dbal": "2.2.*" + }, + "type": "library", + "installation-source": "source", + "license": [ + "LGPL" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/", + "role": null + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": null, + "role": null + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org", + "homepage": null, + "role": null + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de", + "homepage": null, + "role": null + } + ], + "description": "Object-Relational-Mapper for PHP", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "orm" + ], + "autoload": { + "psr-0": { + "Doctrine\\ORM": "lib/" + } + } + }, + { + "name": "doctrine/dbal", + "version": "2.2.x-dev", + "version_normalized": "2.2.9999999.9999999-dev", + "time": "2012-05-25 20:08:36", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal", + "reference": "8cc129aa64a8de6447056bce20f0a274fe2a340b" + }, + "dist": { + "type": "zip", + "url": "https://github.com/doctrine/dbal/zipball/8cc129aa64a8de6447056bce20f0a274fe2a340b", + "reference": "8cc129aa64a8de6447056bce20f0a274fe2a340b", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "doctrine/common": ">=2.2.0,<=2.2.99" + }, + "type": "library", + "installation-source": "source", + "license": [ + "LGPL" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/", + "role": null + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": null, + "role": null + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org", + "homepage": null, + "role": null + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de", + "homepage": null, + "role": null + } + ], + "description": "Database Abstraction Layer", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "persistence", + "dbal", + "queryobject" + ], + "autoload": { + "psr-0": { + "Doctrine\\DBAL": "lib/" + } + } + }, + { + "name": "twig/twig", + "version": "dev-master", + "version_normalized": "9999999-dev", + "time": "2012-06-16 08:52:23", + "source": { + "type": "git", + "url": "git://github.com/fabpot/Twig.git", + "reference": "4679ad51c5390648b7ea4c8f0ecd2c0c344145ba" + }, + "dist": { + "type": "zip", + "url": "https://github.com/fabpot/Twig/zipball/4679ad51c5390648b7ea4c8f0ecd2c0c344145ba", + "reference": "4679ad51c5390648b7ea4c8f0ecd2c0c344145ba", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "installation-source": "source", + "license": [ + "BSD-3" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": null, + "role": null + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "homepage": null, + "role": null + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "http://twig.sensiolabs.org", + "keywords": [ + "templating" + ], + "autoload": { + "psr-0": { + "Twig_": "lib/" + } + } + }, + { + "name": "doctrine/doctrine-bundle", + "version": "dev-master", + "version_normalized": "9999999-dev", + "target-dir": "Doctrine/Bundle/DoctrineBundle", + "time": "2012-06-18 06:36:07", + "source": { + "type": "git", + "url": "git://github.com/doctrine/DoctrineBundle.git", + "reference": "94951737d6c692500d6b13bb92871935568a4ba5" + }, + "dist": { + "type": "zip", + "url": "https://github.com/doctrine/DoctrineBundle/zipball/94951737d6c692500d6b13bb92871935568a4ba5", + "reference": "94951737d6c692500d6b13bb92871935568a4ba5", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "doctrine/dbal": ">=2.2,<2.4-dev", + "symfony/framework-bundle": "2.1.*", + "symfony/doctrine-bridge": "2.1.*" + }, + "require-dev": { + "doctrine/orm": ">=2.2,<2.4-dev", + "symfony/validator": "2.1.*", + "symfony/yaml": "2.1.*" + }, + "suggest": { + "doctrine/orm": "The Doctrine ORM integration is optional in the bundle." + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "source", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": null, + "role": null + }, + { + "name": "Symfony Community", + "email": null, + "homepage": "http://symfony.com/contributors", + "role": null + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de", + "homepage": null, + "role": null + } + ], + "description": "Symfony DoctrineBundle", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "orm", + "persistence", + "dbal" + ], + "autoload": { + "psr-0": { + "Doctrine\\Bundle\\DoctrineBundle": "" + } + } + }, + { + "name": "sensio/generator-bundle", + "version": "dev-master", + "version_normalized": "9999999-dev", + "target-dir": "Sensio/Bundle/GeneratorBundle", + "time": "2012-06-18 16:40:45", + "source": { + "type": "git", + "url": "https://github.com/sensio/SensioGeneratorBundle", + "reference": "43ed45c48db18e4a0e48aec0c098f42e56e22d36" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sensio/SensioGeneratorBundle/zipball/43ed45c48db18e4a0e48aec0c098f42e56e22d36", + "reference": "43ed45c48db18e4a0e48aec0c098f42e56e22d36", + "shasum": "" + }, + "require": { + "symfony/framework-bundle": "2.1.*" + }, + "require-dev": { + "symfony/doctrine-bridge": "2.1.*", + "doctrine/orm": ">=2.1,<2.4-dev", + "twig/twig": ">=1.8,<2.0-dev" + }, + "suggest": { + "doctrine/doctrine-bundle": "to generate entities and their crud controller" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "installation-source": "source", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": null, + "role": null + } + ], + "description": "This bundle generates code for you", + "autoload": { + "psr-0": { + "Sensio\\Bundle\\GeneratorBundle": "" + } + } + }, + { + "name": "sensio/framework-extra-bundle", + "version": "dev-master", + "version_normalized": "9999999-dev", + "target-dir": "Sensio/Bundle/FrameworkExtraBundle", + "time": "2012-06-18 10:41:18", + "source": { + "type": "git", + "url": "https://github.com/sensio/SensioFrameworkExtraBundle", + "reference": "62e41b85947034b0f1dfe31bb8e76920e1488571" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sensio/SensioFrameworkExtraBundle/zipball/62e41b85947034b0f1dfe31bb8e76920e1488571", + "reference": "62e41b85947034b0f1dfe31bb8e76920e1488571", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.1,<2.4-dev", + "symfony/framework-bundle": "2.1.*" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "installation-source": "source", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": null, + "role": null + } + ], + "description": "This bundle provides a way to configure your controllers with annotations", + "keywords": [ + "annotations", + "controllers" + ], + "autoload": { + "psr-0": { + "Sensio\\Bundle\\FrameworkExtraBundle": "" + } + } + }, + { + "name": "symfony/assetic-bundle", + "version": "dev-master", + "version_normalized": "9999999-dev", + "target-dir": "Symfony/Bundle/AsseticBundle", + "time": "2012-06-19 21:29:31", + "source": { + "type": "git", + "url": "https://github.com/symfony/AsseticBundle", + "reference": "8fe7b898b08103c1d6fce64c3e279a7afd61adfc" + }, + "dist": { + "type": "zip", + "url": "https://github.com/symfony/AsseticBundle/zipball/8fe7b898b08103c1d6fce64c3e279a7afd61adfc", + "reference": "8fe7b898b08103c1d6fce64c3e279a7afd61adfc", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "symfony/framework-bundle": "2.1.*", + "kriswallsmith/assetic": "1.1.*" + }, + "suggest": { + "symfony/twig-bundle": "2.1.*" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "installation-source": "source", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/", + "role": null + } + ], + "description": "Integrates Assetic into Symfony2", + "homepage": "https://github.com/symfony/AsseticBundle", + "keywords": [ + "assets", + "compression", + "minification" + ], + "autoload": { + "psr-0": { + "Symfony\\Bundle\\AsseticBundle": "" + } + } + }, + { + "name": "symfony/swiftmailer-bundle", + "version": "dev-master", + "version_normalized": "9999999-dev", + "target-dir": "Symfony/Bundle/SwiftmailerBundle", + "time": "2012-06-18 20:25:44", + "source": { + "type": "git", + "url": "https://github.com/symfony/SwiftmailerBundle", + "reference": "d05c9c514a631ee688c53c4cc5505da757bd50d3" + }, + "dist": { + "type": "zip", + "url": "https://github.com/symfony/SwiftmailerBundle/zipball/d05c9c514a631ee688c53c4cc5505da757bd50d3", + "reference": "d05c9c514a631ee688c53c4cc5505da757bd50d3", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "symfony/swiftmailer-bridge": "self.version", + "swiftmailer/swiftmailer": ">=4.1.8,<4.2-dev" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "installation-source": "source", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": null, + "role": null + }, + { + "name": "Symfony Community", + "email": null, + "homepage": "http://symfony.com/contributors", + "role": null + } + ], + "description": "Symfony SwiftmailerBundle", + "homepage": "http://symfony.com", + "autoload": { + "psr-0": { + "Symfony\\Bundle\\SwiftmailerBundle": "" + } + } + }, + { + "name": "swiftmailer/swiftmailer", + "version": "dev-master", + "version_normalized": "9999999-dev", + "time": "2012-06-20 14:46:20", + "source": { + "type": "git", + "url": "git://github.com/swiftmailer/swiftmailer.git", + "reference": "d57ffdeed664d6061cef0047e1f5d3fc3ee3fb99" + }, + "dist": { + "type": "zip", + "url": "https://github.com/swiftmailer/swiftmailer/zipball/d57ffdeed664d6061cef0047e1f5d3fc3ee3fb99", + "reference": "d57ffdeed664d6061cef0047e1f5d3fc3ee3fb99", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "installation-source": "source", + "license": [ + "LGPL" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": null, + "role": null + }, + { + "name": "Chris Corbyn", + "email": "", + "homepage": null, + "role": null + } + ], + "description": "Swiftmailer, free feature-rich PHP mailer", + "homepage": "http://swiftmailer.org", + "keywords": [ + "mail", + "mailer" + ], + "autoload": { + "files": [ + "lib/swift_required.php" + ] + } + }, + { + "name": "symfony/symfony", + "version": "dev-master", + "version_normalized": "9999999-dev", + "time": "2012-06-21 09:42:20", + "source": { + "type": "git", + "url": "git://github.com/symfony/symfony.git", + "reference": "v2.1.0-BETA1" + }, + "dist": { + "type": "zip", + "url": "https://github.com/symfony/symfony/zipball/v2.1.0-BETA1", + "reference": "v2.1.0-BETA1", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "twig/twig": ">=1.8,<2.0-dev", + "doctrine/common": ">2.2,<2.4-dev" + }, + "replace": { + "symfony/doctrine-bridge": "self.version", + "symfony/monolog-bridge": "self.version", + "symfony/twig-bridge": "self.version", + "symfony/framework-bundle": "self.version", + "symfony/security-bundle": "self.version", + "symfony/twig-bundle": "self.version", + "symfony/web-profiler-bundle": "self.version", + "symfony/browser-kit": "self.version", + "symfony/class-loader": "self.version", + "symfony/config": "self.version", + "symfony/console": "self.version", + "symfony/css-selector": "self.version", + "symfony/dependency-injection": "self.version", + "symfony/dom-crawler": "self.version", + "symfony/event-dispatcher": "self.version", + "symfony/filesystem": "self.version", + "symfony/finder": "self.version", + "symfony/form": "self.version", + "symfony/http-foundation": "self.version", + "symfony/http-kernel": "self.version", + "symfony/locale": "self.version", + "symfony/process": "self.version", + "symfony/routing": "self.version", + "symfony/security": "self.version", + "symfony/serializer": "self.version", + "symfony/templating": "self.version", + "symfony/translation": "self.version", + "symfony/validator": "self.version", + "symfony/yaml": "self.version", + "symfony/propel1-bridge": "self.version", + "symfony/options-resolver": "self.version", + "symfony/swiftmailer-bridge": "self.version" + }, + "require-dev": { + "doctrine/data-fixtures": "1.0.*", + "propel/propel1": "dev-master", + "monolog/monolog": "dev-master", + "doctrine/dbal": ">=2.2,<2.4-dev", + "doctrine/orm": ">=2.2,<2.4-dev" + }, + "suggest": { + "doctrine/data-fixtures": "1.0.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "installation-source": "source", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": null, + "role": null + }, + { + "name": "Symfony Community", + "email": null, + "homepage": "http://symfony.com/contributors", + "role": null + } + ], + "description": "The Symfony PHP framework", + "homepage": "http://symfony.com", + "keywords": [ + "framework" + ], + "autoload": { + "psr-0": { + "Symfony": "src/", + "SessionHandlerInterface": "src/Symfony/Component/HttpFoundation/Resources/stubs" + } + } + }, + { + "name": "sensio/distribution-bundle", + "version": "dev-master", + "version_normalized": "9999999-dev", + "target-dir": "Sensio/Bundle/DistributionBundle", + "time": "2012-06-21 15:12:48", + "source": { + "type": "git", + "url": "https://github.com/sensio/SensioDistributionBundle", + "reference": "a360ad61fe34206a7295c1ef00b5455b2b2e1071" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sensio/SensioDistributionBundle/zipball/a360ad61fe34206a7295c1ef00b5455b2b2e1071", + "reference": "a360ad61fe34206a7295c1ef00b5455b2b2e1071", + "shasum": "" + }, + "require": { + "symfony/framework-bundle": "2.1.*" + }, + "type": "symfony-bundle", + "installation-source": "source", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": null, + "role": null + } + ], + "description": "The base bundle for the Symfony Distributions", + "keywords": [ + "distribution", + "configuration" + ], + "autoload": { + "psr-0": { + "Sensio\\Bundle\\DistributionBundle": "" + } + } + } +] diff --git a/vendor/composer/installed_dev.json b/vendor/composer/installed_dev.json new file mode 100644 index 0000000..3be7d3e --- /dev/null +++ b/vendor/composer/installed_dev.json @@ -0,0 +1,3 @@ +[ + + ] diff --git a/vendor/doctrine/common/.travis.yml b/vendor/doctrine/common/.travis.yml new file mode 100644 index 0000000..4464d4f --- /dev/null +++ b/vendor/doctrine/common/.travis.yml @@ -0,0 +1,5 @@ +language: php + +php: + - 5.3 + - 5.4 diff --git a/vendor/doctrine/common/LICENSE b/vendor/doctrine/common/LICENSE new file mode 100644 index 0000000..1c03f74 --- /dev/null +++ b/vendor/doctrine/common/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/vendor/doctrine/common/README.md b/vendor/doctrine/common/README.md new file mode 100644 index 0000000..c63f762 --- /dev/null +++ b/vendor/doctrine/common/README.md @@ -0,0 +1,12 @@ +# Doctrine Common + +[![Build Status](https://secure.travis-ci.org/doctrine/common.png)](http://travis-ci.org/doctrine/common) + +The Doctrine Common project is a library that provides extensions to core PHP functionality. + +## More resources: + +* [Website](http://www.doctrine-project.org) +* [Documentation](http://www.doctrine-project.org/projects/common/current/docs/en) +* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DCOM) +* [Downloads](http://github.com/doctrine/common/downloads) diff --git a/vendor/doctrine/common/UPGRADE_TO_2_1 b/vendor/doctrine/common/UPGRADE_TO_2_1 new file mode 100644 index 0000000..891a2e5 --- /dev/null +++ b/vendor/doctrine/common/UPGRADE_TO_2_1 @@ -0,0 +1,39 @@ +This document details all the possible changes that you should investigate when updating +your project from Doctrine Common 2.0.x to 2.1 + +## AnnotationReader changes + +The annotation reader was heavily refactored between 2.0 and 2.1-RC1. In theory the operation of the new reader should be backwards compatible, but it has to be setup differently to work that way: + + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + $reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\'); + // new code necessary starting here + $reader->setIgnoreNotImportedAnnotations(true); + $reader->setEnableParsePhpImports(false); + $reader = new \Doctrine\Common\Annotations\CachedReader( + new \Doctrine\Common\Annotations\IndexedReader($reader), new ArrayCache() + ); + +## Annotation Base class or @Annotation + +Beginning after 2.1-RC2 you have to either extend ``Doctrine\Common\Annotations\Annotation`` or add @Annotation to your annotations class-level docblock, otherwise the class will simply be ignored. + +## Removed methods on AnnotationReader + +* AnnotationReader::setAutoloadAnnotations() +* AnnotationReader::getAutoloadAnnotations() +* AnnotationReader::isAutoloadAnnotations() + +## AnnotationRegistry + +Autoloading through the PHP autoloader is removed from the 2.1 AnnotationReader. Instead you have to use the global AnnotationRegistry for loading purposes: + + \Doctrine\Common\Annotations\AnnotationRegistry::registerFile($fileWithAnnotations); + \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace($namespace, $dirs = null); + \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespaces($namespaces); + \Doctrine\Common\Annotations\AnnotationRegistry::registerLoader($callable); + +The $callable for registering a loader accepts a class as first and only parameter and must try to silently autoload it. On success true has to be returned. +The registerAutoloadNamespace function registers a PSR-0 compatible silent autoloader for all classes with the given namespace in the given directories. +If null is passed as directory the include path will be used. + diff --git a/vendor/doctrine/common/UPGRADE_TO_2_2 b/vendor/doctrine/common/UPGRADE_TO_2_2 new file mode 100644 index 0000000..1d93a13 --- /dev/null +++ b/vendor/doctrine/common/UPGRADE_TO_2_2 @@ -0,0 +1,61 @@ +This document details all the possible changes that you should investigate when +updating your project from Doctrine Common 2.1 to 2.2: + +## Annotation Changes + +- AnnotationReader::setIgnoreNotImportedAnnotations has been removed, you need to + add ignore annotation names which are supposed to be ignored via + AnnotationReader::addGlobalIgnoredName + +- AnnotationReader::setAutoloadAnnotations was deprecated by the AnnotationRegistry + in 2.1 and has been removed in 2.2 + +- AnnotationReader::setEnableParsePhpImports was added to ease transition to the new + annotation mechanism in 2.1 and is removed in 2.2 + +- AnnotationReader::isParsePhpImportsEnabled is removed (see above) + +- AnnotationReader::setDefaultAnnotationNamespace was deprecated in favor of explicit + configuration in 2.1 and will be removed in 2.2 (for isolated projects where you + have full-control over _all_ available annotations, we offer a dedicated reader + class ``SimpleAnnotationReader``) + +- AnnotationReader::setAnnotationCreationFunction was deprecated in 2.1 and will be + removed in 2.2. We only offer two creation mechanisms which cannot be changed + anymore to allow the same reader instance to work with all annotations regardless + of which library they are coming from. + +- AnnotationReader::setAnnotationNamespaceAlias was deprecated in 2.1 and will be + removed in 2.2 (see setDefaultAnnotationNamespace) + +- If you use a class as annotation which has not the @Annotation marker in it's + class block, we will now throw an exception instead of silently ignoring it. You + can however still achieve the previous behavior using the @IgnoreAnnotation, or + AnnotationReader::addGlobalIgnoredName (the exception message will contain detailed + instructions when you run into this problem). + +## Cache Changes + +- Renamed old AbstractCache to CacheProvider + +- Dropped the support to the following functions of all cache providers: + + - CacheProvider::deleteByWildcard + + - CacheProvider::deleteByRegEx + + - CacheProvider::deleteByPrefix + + - CacheProvider::deleteBySuffix + +- CacheProvider::deleteAll will not remove ALL entries, it will only mark them as invalid + +- CacheProvider::flushAll will remove ALL entries, namespaced or not + +- Added support to MemcachedCache + +- Added support to WincacheCache + +## ClassLoader Changes + +- ClassLoader::fileExistsInIncludePath() no longer exists. Use the native stream_resolve_include_path() PHP function \ No newline at end of file diff --git a/vendor/doctrine/common/composer.json b/vendor/doctrine/common/composer.json new file mode 100644 index 0000000..7780d93 --- /dev/null +++ b/vendor/doctrine/common/composer.json @@ -0,0 +1,21 @@ +{ + "name": "doctrine/common", + "type": "library","version":"2.2.2", + "description": "Common Library for Doctrine projects", + "keywords": ["collections", "spl", "eventmanager", "annotations", "persistence"], + "homepage": "http://www.doctrine-project.org", + "license": "LGPL", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Doctrine\\Common": "lib/" } + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation.php new file mode 100644 index 0000000..6251252 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Annotations class + * + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Annotation +{ + /** + * Value property. Common among all derived classes. + * + * @var string + */ + public $value; + + /** + * Constructor + * + * @param array $data Key-value for properties to be defined in this class + */ + public final function __construct(array $data) + { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } + + /** + * Error handler for unknown property accessor in Annotation class. + * + * @param string $name Unknown property name + */ + public function __get($name) + { + throw new \BadMethodCallException( + sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this)) + ); + } + + /** + * Error handler for unknown property mutator in Annotation class. + * + * @param string $name Unkown property name + * @param mixed $value Property value + */ + public function __set($name, $value) + { + throw new \BadMethodCallException( + sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this)) + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Attribute.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Attribute.php new file mode 100644 index 0000000..21597b1 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Attribute.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check the attribute type during the parsing process. + * + * @author Fabio B. Silva + * + * @Annotation + */ +final class Attribute +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $type; + + /** + * @var boolean + */ + public $required = false; +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Attributes.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Attributes.php new file mode 100644 index 0000000..6e314be --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Attributes.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check the types of all declared attributes during the parsing process. + * + * @author Fabio B. Silva + * + * @Annotation + */ +final class Attributes +{ + /** + * @var array + */ + public $value; +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php new file mode 100644 index 0000000..1b2b20a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser to ignore specific + * annotations during the parsing process. + * + * @Annotation + * @author Johannes M. Schmitt + */ +final class IgnoreAnnotation +{ + public $names; + + public function __construct(array $values) + { + if (is_string($values['value'])) { + $values['value'] = array($values['value']); + } + if (!is_array($values['value'])) { + throw new \RuntimeException(sprintf('@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.', json_encode($values['value']))); + } + + $this->names = $values['value']; + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Required.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Required.php new file mode 100644 index 0000000..7b89a02 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Required.php @@ -0,0 +1,33 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check if that attribute is required during the parsing process. + * + * @author Fabio B. Silva + * + * @Annotation + */ +final class Required +{ +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Target.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Target.php new file mode 100644 index 0000000..c41896a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Annotation/Target.php @@ -0,0 +1,105 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check the annotation target during the parsing process. + * + * @author Fabio B. Silva + * + * @Annotation + */ +final class Target +{ + const TARGET_CLASS = 1; + const TARGET_METHOD = 2; + const TARGET_PROPERTY = 4; + const TARGET_ANNOTATION = 8; + const TARGET_ALL = 15; + + /** + * @var array + */ + private static $map = array( + 'ALL' => self::TARGET_ALL, + 'CLASS' => self::TARGET_CLASS, + 'METHOD' => self::TARGET_METHOD, + 'PROPERTY' => self::TARGET_PROPERTY, + 'ANNOTATION' => self::TARGET_ANNOTATION, + ); + + /** + * @var array + */ + public $value; + + /** + * Targets as bitmask. + * + * @var integer + */ + public $targets; + + /** + * Literal target declaration. + * + * @var integer + */ + public $literal; + + /** + * Annotation construct + * + * @param array $values + */ + public function __construct(array $values) + { + if (!isset($values['value'])){ + $values['value'] = null; + } + if (is_string($values['value'])){ + $values['value'] = array($values['value']); + } + if (!is_array($values['value'])){ + throw new \InvalidArgumentException( + sprintf('@Target expects either a string value, or an array of strings, "%s" given.', + is_object($values['value']) ? get_class($values['value']) : gettype($values['value']) + ) + ); + } + + $bitmask = 0; + foreach ($values['value'] as $literal) { + if(!isset(self::$map[$literal])){ + throw new \InvalidArgumentException( + sprintf('Invalid Target "%s". Available targets: [%s]', + $literal, implode(', ', array_keys(self::$map))) + ); + } + $bitmask += self::$map[$literal]; + } + + $this->targets = $bitmask; + $this->value = $values['value']; + $this->literal = implode(', ', $this->value); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationException.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationException.php new file mode 100644 index 0000000..fdc1913 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationException.php @@ -0,0 +1,111 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Description of AnnotationException + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class AnnotationException extends \Exception +{ + /** + * Creates a new AnnotationException describing a Syntax error. + * + * @param string $message Exception message + * @return AnnotationException + */ + public static function syntaxError($message) + { + return new self('[Syntax Error] ' . $message); + } + + /** + * Creates a new AnnotationException describing a Semantical error. + * + * @param string $message Exception message + * @return AnnotationException + */ + public static function semanticalError($message) + { + return new self('[Semantical Error] ' . $message); + } + + /** + * Creates a new AnnotationException describing an error which occurred during + * the creation of the annotation. + * + * @since 2.2 + * @param string $message + * @return AnnotationException + */ + public static function creationError($message) + { + return new self('[Creation Error] ' . $message); + } + + /** + * Creates a new AnnotationException describing an type error of an attribute. + * + * @since 2.2 + * @param string $attributeName + * @param string $annotationName + * @param string $context + * @param string $expected + * @param mixed $actual + * @return AnnotationException + */ + public static function typeError($attributeName, $annotationName, $context, $expected, $actual) + { + return new self(sprintf( + '[Type Error] Attribute "%s" of @%s declared on %s expects %s, but got %s.', + $attributeName, + $annotationName, + $context, + $expected, + is_object($actual) ? 'an instance of '.get_class($actual) : gettype($actual) + )); + } + + /** + * Creates a new AnnotationException describing an required error of an attribute. + * + * @since 2.2 + * @param string $attributeName + * @param string $annotationName + * @param string $context + * @param string $expected + * @return AnnotationException + */ + public static function requiredError($attributeName, $annotationName, $context, $expected) + { + return new self(sprintf( + '[Type Error] Attribute "%s" of @%s declared on %s expects %s. This value should not be null.', + $attributeName, + $annotationName, + $context, + $expected + )); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationReader.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationReader.php new file mode 100644 index 0000000..dda8b23 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationReader.php @@ -0,0 +1,301 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation; +use Doctrine\Common\Annotations\Annotation\Target; +use Closure; +use ReflectionClass; +use ReflectionMethod; +use ReflectionProperty; + +/** + * A reader for docblock annotations. + * + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Johannes M. Schmitt + */ +final class AnnotationReader implements Reader +{ + /** + * Global map for imports. + * + * @var array + */ + private static $globalImports = array( + 'ignoreannotation' => 'Doctrine\Common\Annotations\Annotation\IgnoreAnnotation', + ); + + /** + * A list with annotations that are not causing exceptions when not resolved to an annotation class. + * + * The names are case sensitive. + * + * @var array + */ + private static $globalIgnoredNames = array( + 'access'=> true, 'author'=> true, 'copyright'=> true, 'deprecated'=> true, + 'example'=> true, 'ignore'=> true, 'internal'=> true, 'link'=> true, 'see'=> true, + 'since'=> true, 'tutorial'=> true, 'version'=> true, 'package'=> true, + 'subpackage'=> true, 'name'=> true, 'global'=> true, 'param'=> true, + 'return'=> true, 'staticvar'=> true, 'category'=> true, 'staticVar'=> true, + 'static'=> true, 'var'=> true, 'throws'=> true, 'inheritdoc'=> true, + 'inheritDoc'=> true, 'license'=> true, 'todo'=> true, 'deprecated'=> true, + 'deprec'=> true, 'author'=> true, 'property' => true, 'method' => true, + 'abstract'=> true, 'exception'=> true, 'magic' => true, 'api' => true, + 'final'=> true, 'filesource'=> true, 'throw' => true, 'uses' => true, + 'usedby'=> true, 'private' => true, 'Annotation' => true, 'override' => true, + 'codeCoverageIgnore' => true, 'codeCoverageIgnoreStart' => true, 'codeCoverageIgnoreEnd' => true, + 'Required' => true, 'Attribute' => true, 'Attributes' => true, + 'Target' => true, 'SuppressWarnings' => true, + ); + + /** + * Add a new annotation to the globally ignored annotation names with regard to exception handling. + * + * @param string $name + */ + static public function addGlobalIgnoredName($name) + { + self::$globalIgnoredNames[$name] = true; + } + + /** + * Annotations Parser + * + * @var Doctrine\Common\Annotations\DocParser + */ + private $parser; + + /** + * Annotations Parser used to collect parsing metadata + * + * @var Doctrine\Common\Annotations\DocParser + */ + private $preParser; + + /** + * PHP Parser used to collect imports. + * + * @var Doctrine\Common\Annotations\PhpParser + */ + private $phpParser; + + /** + * In-memory cache mechanism to store imported annotations per class. + * + * @var array + */ + private $imports = array(); + + /** + * In-memory cache mechanism to store ignored annotations per class. + * + * @var array + */ + private $ignoredAnnotationNames = array(); + + /** + * Constructor. + * + * Initializes a new AnnotationReader. + */ + public function __construct() + { + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/IgnoreAnnotation.php'); + + $this->parser = new DocParser; + + $this->preParser = new DocParser; + $this->preParser->setImports(self::$globalImports); + $this->preParser->setIgnoreNotImportedAnnotations(true); + + $this->phpParser = new PhpParser; + } + + /** + * Gets the annotations applied to a class. + * + * @param ReflectionClass $class The ReflectionClass of the class from which + * the class annotations should be read. + * @return array An array of Annotations. + */ + public function getClassAnnotations(ReflectionClass $class) + { + $this->parser->setTarget(Target::TARGET_CLASS); + $this->parser->setImports($this->getImports($class)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); + } + + /** + * Gets a class annotation. + * + * @param ReflectionClass $class The ReflectionClass of the class from which + * the class annotations should be read. + * @param string $annotationName The name of the annotation. + * @return The Annotation or NULL, if the requested annotation does not exist. + */ + public function getClassAnnotation(ReflectionClass $class, $annotationName) + { + $annotations = $this->getClassAnnotations($class); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Gets the annotations applied to a property. + * + * @param ReflectionProperty $property The ReflectionProperty of the property + * from which the annotations should be read. + * @return array An array of Annotations. + */ + public function getPropertyAnnotations(ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $context = 'property ' . $class->getName() . "::\$" . $property->getName(); + $this->parser->setTarget(Target::TARGET_PROPERTY); + $this->parser->setImports($this->getImports($class)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($property->getDocComment(), $context); + } + + /** + * Gets a property annotation. + * + * @param ReflectionProperty $property + * @param string $annotationName The name of the annotation. + * @return The Annotation or NULL, if the requested annotation does not exist. + */ + public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) + { + $annotations = $this->getPropertyAnnotations($property); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Gets the annotations applied to a method. + * + * @param ReflectionMethod $property The ReflectionMethod of the method from which + * the annotations should be read. + * @return array An array of Annotations. + */ + public function getMethodAnnotations(ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $context = 'method ' . $class->getName() . '::' . $method->getName() . '()'; + $this->parser->setTarget(Target::TARGET_METHOD); + $this->parser->setImports($this->getImports($class)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($method->getDocComment(), $context); + } + + /** + * Gets a method annotation. + * + * @param ReflectionMethod $method + * @param string $annotationName The name of the annotation. + * @return The Annotation or NULL, if the requested annotation does not exist. + */ + public function getMethodAnnotation(ReflectionMethod $method, $annotationName) + { + $annotations = $this->getMethodAnnotations($method); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Returns the ignored annotations for the given class. + * + * @param ReflectionClass $class + * @return array + */ + private function getIgnoredAnnotationNames(ReflectionClass $class) + { + if (isset($this->ignoredAnnotationNames[$name = $class->getName()])) { + return $this->ignoredAnnotationNames[$name]; + } + $this->collectParsingMetadata($class); + + return $this->ignoredAnnotationNames[$name]; + } + + private function getImports(ReflectionClass $class) + { + if (isset($this->imports[$name = $class->getName()])) { + return $this->imports[$name]; + } + $this->collectParsingMetadata($class); + + return $this->imports[$name]; + } + + /** + * Collects parsing metadata for a given class + * + * @param ReflectionClass $class + */ + private function collectParsingMetadata(ReflectionClass $class) + { + $ignoredAnnotationNames = self::$globalIgnoredNames; + + $annotations = $this->preParser->parse($class->getDocComment(), 'class '.$class->name); + foreach ($annotations as $annotation) { + if ($annotation instanceof IgnoreAnnotation) { + foreach ($annotation->names AS $annot) { + $ignoredAnnotationNames[$annot] = true; + } + } + } + + $name = $class->getName(); + $this->imports[$name] = array_merge( + self::$globalImports, + $this->phpParser->parseClass($class), + array('__NAMESPACE__' => $class->getNamespaceName()) + ); + $this->ignoredAnnotationNames[$name] = $ignoredAnnotationNames; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationRegistry.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationRegistry.php new file mode 100644 index 0000000..60b00f5 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationRegistry.php @@ -0,0 +1,129 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +final class AnnotationRegistry +{ + /** + * A map of namespaces to use for autoloading purposes based on a PSR-0 convention. + * + * Contains the namespace as key and an array of directories as value. If the value is NULL + * the include path is used for checking for the corresponding file. + * + * This autoloading mechanism does not utilize the PHP autoloading but implements autoloading on its own. + * + * @var array + */ + static private $autoloadNamespaces = array(); + + /** + * A map of autoloader callables. + * + * @var array + */ + static private $loaders = array(); + + static public function reset() + { + self::$autoloadNamespaces = array(); + self::$loaders = array(); + } + + static public function registerFile($file) + { + require_once $file; + } + + /** + * Add a namespace with one or many directories to look for files or null for the include path. + * + * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm. + * + * @param string $namespace + * @param string|array|null $dirs + */ + static public function registerAutoloadNamespace($namespace, $dirs = null) + { + self::$autoloadNamespaces[$namespace] = $dirs; + } + + /** + * Register multiple namespaces + * + * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm. + * + * @param array $namespaces + */ + static public function registerAutoloadNamespaces(array $namespaces) + { + self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces); + } + + /** + * Register an autoloading callabale for annotations, much like spl_autoload_register(). + * + * NOTE: These class loaders HAVE to be silent when a class was not found! + * IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class. + * + * @param callabale $callabale + */ + static public function registerLoader($callabale) + { + if (!is_callable($callabale)) { + throw new \InvalidArgumentException("A callable is expected in AnnotationRegistry::registerLoader()."); + } + self::$loaders[] = $callabale; + } + + /** + * Autoload an annotation class silently. + * + * @param string $class + * @return void + */ + static public function loadAnnotationClass($class) + { + foreach (self::$autoloadNamespaces AS $namespace => $dirs) { + if (strpos($class, $namespace) === 0) { + $file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php"; + if ($dirs === null) { + if ($path = stream_resolve_include_path($file)) { + require $path; + return true; + } + } else { + foreach((array)$dirs AS $dir) { + if (file_exists($dir . DIRECTORY_SEPARATOR . $file)) { + require $dir . DIRECTORY_SEPARATOR . $file; + return true; + } + } + } + } + } + + foreach (self::$loaders AS $loader) { + if (call_user_func($loader, $class) === true) { + return true; + } + } + return false; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/CachedReader.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/CachedReader.php new file mode 100644 index 0000000..6ea47c6 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/CachedReader.php @@ -0,0 +1,179 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Cache\Cache; + +/** + * A cache aware annotation reader. + * + * @author Johannes M. Schmitt + * @author Benjamin Eberlei + */ +final class CachedReader implements Reader +{ + private static $CACHE_SALT = '@[Annot]'; + + /** + * @var Reader + */ + private $delegate; + + /** + * @var Cache + */ + private $cache; + + /** + * @var boolean + */ + private $debug; + + /** + * @var array + */ + private $loadedAnnotations; + + /** + * @param Reader $reader + * @param Cache $cache + */ + public function __construct(Reader $reader, Cache $cache, $debug = false) + { + $this->delegate = $reader; + $this->cache = $cache; + $this->debug = $debug; + } + + public function getClassAnnotations(\ReflectionClass $class) + { + $cacheKey = $class->getName() . self::$CACHE_SALT; + + if (isset($this->loadedAnnotations[$cacheKey])) { + return $this->loadedAnnotations[$cacheKey]; + } + + // Attempt to grab data from cache + if (($data = $this->cache->fetch($cacheKey)) !== false) { + if (!$this->debug || $this->isCacheFresh($cacheKey, $class)) { + return $data; + } + } + + $annots = $this->delegate->getClassAnnotations($class); + $this->cache->save($cacheKey, $annots); + $this->cache->save('[C]'.$cacheKey, time()); + + return $this->loadedAnnotations[$cacheKey] = $annots; + } + + public function getClassAnnotation(\ReflectionClass $class, $annotationName) + { + foreach ($this->getClassAnnotations($class) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $cacheKey = $class->getName().'$'.$property->getName().self::$CACHE_SALT; + + if (isset($this->loadedAnnotations[$cacheKey])) { + return $this->loadedAnnotations[$cacheKey]; + } + + // Attempt to grab data from cache + if (($data = $this->cache->fetch($cacheKey)) !== false) { + if (!$this->debug || $this->isCacheFresh($cacheKey, $class)) { + return $data; + } + } + + $annots = $this->delegate->getPropertyAnnotations($property); + $this->cache->save($cacheKey, $annots); + $this->cache->save('[C]'.$cacheKey, time()); + + return $this->loadedAnnotations[$cacheKey] = $annots; + } + + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + foreach ($this->getPropertyAnnotations($property) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + public function getMethodAnnotations(\ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $cacheKey = $class->getName().'#'.$method->getName().self::$CACHE_SALT; + + if (isset($this->loadedAnnotations[$cacheKey])) { + return $this->loadedAnnotations[$cacheKey]; + } + + // Attempt to grab data from cache + if (($data = $this->cache->fetch($cacheKey)) !== false) { + if (!$this->debug || $this->isCacheFresh($cacheKey, $class)) { + return $data; + } + } + + $annots = $this->delegate->getMethodAnnotations($method); + $this->cache->save($cacheKey, $annots); + $this->cache->save('[C]'.$cacheKey, time()); + + return $this->loadedAnnotations[$cacheKey] = $annots; + } + + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + foreach ($this->getMethodAnnotations($method) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + public function clearLoadedAnnotations() + { + $this->loadedAnnotations = array(); + } + + private function isCacheFresh($cacheKey, \ReflectionClass $class) + { + if (false === $filename = $class->getFilename()) { + return true; + } + + return $this->cache->fetch('[C]'.$cacheKey) >= filemtime($filename); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/DocLexer.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/DocLexer.php new file mode 100644 index 0000000..c6223e3 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/DocLexer.php @@ -0,0 +1,140 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Lexer; + +/** + * Simple lexer for docblock annotations. + * + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Johannes M. Schmitt + */ +final class DocLexer extends Lexer +{ + const T_NONE = 1; + const T_IDENTIFIER = 2; + const T_INTEGER = 3; + const T_STRING = 4; + const T_FLOAT = 5; + + const T_AT = 101; + const T_CLOSE_CURLY_BRACES = 102; + const T_CLOSE_PARENTHESIS = 103; + const T_COMMA = 104; + const T_EQUALS = 105; + const T_FALSE = 106; + const T_NAMESPACE_SEPARATOR = 107; + const T_OPEN_CURLY_BRACES = 108; + const T_OPEN_PARENTHESIS = 109; + const T_TRUE = 110; + const T_NULL = 111; + const T_COLON = 112; + + /** + * @inheritdoc + */ + protected function getCatchablePatterns() + { + return array( + '[a-z_][a-z0-9_:]*', + '(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?', + '"(?:[^"]|"")*"', + ); + } + + /** + * @inheritdoc + */ + protected function getNonCatchablePatterns() + { + return array('\s+', '\*+', '(.)'); + } + + /** + * @inheritdoc + */ + protected function getType(&$value) + { + $type = self::T_NONE; + + // Checking numeric value + if (is_numeric($value)) { + return (strpos($value, '.') !== false || stripos($value, 'e') !== false) + ? self::T_FLOAT : self::T_INTEGER; + } + + if ($value[0] === '"') { + $value = str_replace('""', '"', substr($value, 1, strlen($value) - 2)); + + return self::T_STRING; + } else { + switch (strtolower($value)) { + case '@': + return self::T_AT; + + case ',': + return self::T_COMMA; + + case '(': + return self::T_OPEN_PARENTHESIS; + + case ')': + return self::T_CLOSE_PARENTHESIS; + + case '{': + return self::T_OPEN_CURLY_BRACES; + + case '}': + return self::T_CLOSE_CURLY_BRACES; + + case '=': + return self::T_EQUALS; + + case '\\': + return self::T_NAMESPACE_SEPARATOR; + + case 'true': + return self::T_TRUE; + + case 'false': + return self::T_FALSE; + + case 'null': + return self::T_NULL; + + case ':': + return self::T_COLON; + + default: + if (ctype_alpha($value[0]) || $value[0] === '_') { + return self::T_IDENTIFIER; + } + + break; + } + } + + return $type; + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/DocParser.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/DocParser.php new file mode 100644 index 0000000..9d16b17 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/DocParser.php @@ -0,0 +1,891 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Closure; +use ReflectionClass; +use Doctrine\Common\Annotations\Annotation\Target; +use Doctrine\Common\Annotations\Annotation\Attribute; +use Doctrine\Common\Annotations\Annotation\Attributes; + +/** + * A parser for docblock annotations. + * + * It is strongly discouraged to change the default annotation parsing process. + * + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Johannes M. Schmitt + * @author Fabio B. Silva + */ +final class DocParser +{ + /** + * An array of all valid tokens for a class name. + * + * @var array + */ + private static $classIdentifiers = array(DocLexer::T_IDENTIFIER, DocLexer::T_TRUE, DocLexer::T_FALSE, DocLexer::T_NULL); + + /** + * The lexer. + * + * @var Doctrine\Common\Annotations\DocLexer + */ + private $lexer; + + /** + * Current target context + * + * @var string + */ + private $target; + + /** + * Doc Parser used to collect annotation target + * + * @var Doctrine\Common\Annotations\DocParser + */ + private static $metadataParser; + + /** + * Flag to control if the current annotation is nested or not. + * + * @var boolean + */ + private $isNestedAnnotation = false; + + /** + * Hashmap containing all use-statements that are to be used when parsing + * the given doc block. + * + * @var array + */ + private $imports = array(); + + /** + * This hashmap is used internally to cache results of class_exists() + * look-ups. + * + * @var array + */ + private $classExists = array(); + + /** + * Whether annotations that have not been imported should be ignored. + * + * @var boolean + */ + private $ignoreNotImportedAnnotations = false; + + /** + * An array of default namespaces if operating in simple mode. + * + * @var array + */ + private $namespaces = array(); + + /** + * A list with annotations that are not causing exceptions when not resolved to an annotation class. + * + * The names must be the raw names as used in the class, not the fully qualified + * class names. + * + * @var array + */ + private $ignoredAnnotationNames = array(); + + /** + * @var string + */ + private $context = ''; + + /** + * Hash-map for caching annotation metadata + * @var array + */ + private static $annotationMetadata = array( + 'Doctrine\Common\Annotations\Annotation\Target' => array( + 'is_annotation' => true, + 'has_constructor' => true, + 'properties' => array(), + 'targets_literal' => 'ANNOTATION_CLASS', + 'targets' => Target::TARGET_CLASS, + 'default_property' => 'value', + 'attribute_types' => array( + 'value' => array( + 'required' => false, + 'type' =>'array', + 'array_type'=>'string', + 'value' =>'array' + ) + ), + ), + 'Doctrine\Common\Annotations\Annotation\Attribute' => array( + 'is_annotation' => true, + 'has_constructor' => false, + 'targets_literal' => 'ANNOTATION_ANNOTATION', + 'targets' => Target::TARGET_ANNOTATION, + 'default_property' => 'name', + 'properties' => array( + 'name' => 'name', + 'type' => 'type', + 'required' => 'required' + ), + 'attribute_types' => array( + 'value' => array( + 'required' => true, + 'type' =>'string', + 'value' =>'string' + ), + 'type' => array( + 'required' =>true, + 'type' =>'string', + 'value' =>'string' + ), + 'required' => array( + 'required' =>false, + 'type' =>'boolean', + 'value' =>'boolean' + ) + ), + ), + 'Doctrine\Common\Annotations\Annotation\Attributes' => array( + 'is_annotation' => true, + 'has_constructor' => false, + 'targets_literal' => 'ANNOTATION_CLASS', + 'targets' => Target::TARGET_CLASS, + 'default_property' => 'value', + 'properties' => array( + 'value' => 'value' + ), + 'attribute_types' => array( + 'value' => array( + 'type' =>'array', + 'required' =>true, + 'array_type'=>'Doctrine\Common\Annotations\Annotation\Attribute', + 'value' =>'array' + ) + ), + ), + ); + + /** + * Hash-map for handle types declaration + * + * @var array + */ + private static $typeMap = array( + 'float' => 'double', + 'bool' => 'boolean', + // allow uppercase Boolean in honor of George Boole + 'Boolean' => 'boolean', + 'int' => 'integer', + ); + + /** + * Constructs a new DocParser. + */ + public function __construct() + { + $this->lexer = new DocLexer; + } + + /** + * Sets the annotation names that are ignored during the parsing process. + * + * The names are supposed to be the raw names as used in the class, not the + * fully qualified class names. + * + * @param array $names + */ + public function setIgnoredAnnotationNames(array $names) + { + $this->ignoredAnnotationNames = $names; + } + + public function setIgnoreNotImportedAnnotations($bool) + { + $this->ignoreNotImportedAnnotations = (Boolean) $bool; + } + + /** + * Sets the default namespaces. + * @param array $namespaces + */ + public function addNamespace($namespace) + { + if ($this->imports) { + throw new \RuntimeException('You must either use addNamespace(), or setImports(), but not both.'); + } + $this->namespaces[] = $namespace; + } + + public function setImports(array $imports) + { + if ($this->namespaces) { + throw new \RuntimeException('You must either use addNamespace(), or setImports(), but not both.'); + } + $this->imports = $imports; + } + + /** + * Sets current target context as bitmask. + * + * @param integer $target + */ + public function setTarget($target) + { + $this->target = $target; + } + + /** + * Parses the given docblock string for annotations. + * + * @param string $input The docblock string to parse. + * @param string $context The parsing context. + * @return array Array of annotations. If no annotations are found, an empty array is returned. + */ + public function parse($input, $context = '') + { + if (false === $pos = strpos($input, '@')) { + return array(); + } + + // also parse whatever character is before the @ + if ($pos > 0) { + $pos -= 1; + } + + $this->context = $context; + $this->lexer->setInput(trim(substr($input, $pos), '* /')); + $this->lexer->moveNext(); + + return $this->Annotations(); + } + + /** + * Attempts to match the given token with the current lookahead token. + * If they match, updates the lookahead token; otherwise raises a syntax error. + * + * @param int Token type. + * @return bool True if tokens match; false otherwise. + */ + private function match($token) + { + if ( ! $this->lexer->isNextToken($token) ) { + $this->syntaxError($this->lexer->getLiteral($token)); + } + + return $this->lexer->moveNext(); + } + + /** + * Attempts to match the current lookahead token with any of the given tokens. + * + * If any of them matches, this method updates the lookahead token; otherwise + * a syntax error is raised. + * + * @param array $tokens + * @return bool + */ + private function matchAny(array $tokens) + { + if ( ! $this->lexer->isNextTokenAny($tokens)) { + $this->syntaxError(implode(' or ', array_map(array($this->lexer, 'getLiteral'), $tokens))); + } + + return $this->lexer->moveNext(); + } + + /** + * Generates a new syntax error. + * + * @param string $expected Expected string. + * @param array $token Optional token. + * @throws SyntaxException + */ + private function syntaxError($expected, $token = null) + { + if ($token === null) { + $token = $this->lexer->lookahead; + } + + $message = "Expected {$expected}, got "; + + if ($this->lexer->lookahead === null) { + $message .= 'end of string'; + } else { + $message .= "'{$token['value']}' at position {$token['position']}"; + } + + if (strlen($this->context)) { + $message .= ' in ' . $this->context; + } + + $message .= '.'; + + throw AnnotationException::syntaxError($message); + } + + /** + * Attempt to check if a class exists or not. This never goes through the PHP autoloading mechanism + * but uses the {@link AnnotationRegistry} to load classes. + * + * @param string $fqcn + * @return boolean + */ + private function classExists($fqcn) + { + if (isset($this->classExists[$fqcn])) { + return $this->classExists[$fqcn]; + } + + // first check if the class already exists, maybe loaded through another AnnotationReader + if (class_exists($fqcn, false)) { + return $this->classExists[$fqcn] = true; + } + + // final check, does this class exist? + return $this->classExists[$fqcn] = AnnotationRegistry::loadAnnotationClass($fqcn); + } + + /** + * Collects parsing metadata for a given annotation class + * + * @param string $name The annotation name + */ + private function collectAnnotationMetadata($name) + { + if (self::$metadataParser == null){ + self::$metadataParser = new self(); + self::$metadataParser->setTarget(Target::TARGET_CLASS); + self::$metadataParser->setIgnoreNotImportedAnnotations(true); + self::$metadataParser->setImports(array( + 'target' => 'Doctrine\Common\Annotations\Annotation\Target', + 'attribute' => 'Doctrine\Common\Annotations\Annotation\Attribute', + 'attributes' => 'Doctrine\Common\Annotations\Annotation\Attributes' + )); + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Target.php'); + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Attribute.php'); + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Attributes.php'); + } + + $class = new \ReflectionClass($name); + $docComment = $class->getDocComment(); + + // Sets default values for annotation metadata + $metadata = array( + 'default_property' => null, + 'has_constructor' => (null !== $constructor = $class->getConstructor()) && $constructor->getNumberOfParameters() > 0, + 'properties' => array(), + 'property_types' => array(), + 'attribute_types' => array(), + 'targets_literal' => null, + 'targets' => Target::TARGET_ALL, + 'is_annotation' => false !== strpos($docComment, '@Annotation'), + ); + + // verify that the class is really meant to be an annotation + if ($metadata['is_annotation']) { + foreach (self::$metadataParser->parse($docComment, 'class @' . $name) as $annotation) { + if ($annotation instanceof Target) { + $metadata['targets'] = $annotation->targets; + $metadata['targets_literal'] = $annotation->literal; + + } elseif ($annotation instanceof Attributes) { + foreach ($annotation->value as $attrib) { + // handle internal type declaration + $type = isset(self::$typeMap[$attrib->type]) ? self::$typeMap[$attrib->type] : $attrib->type; + + // handle the case if the property type is mixed + if ('mixed' !== $type) { + // Checks if the property has array + if (false !== $pos = strpos($type, '<')) { + $arrayType = substr($type, $pos+1, -1); + $type = 'array'; + + if (isset(self::$typeMap[$arrayType])) { + $arrayType = self::$typeMap[$arrayType]; + } + + $metadata['attribute_types'][$attrib->name]['array_type'] = $arrayType; + } + + $metadata['attribute_types'][$attrib->name]['type'] = $type; + $metadata['attribute_types'][$attrib->name]['value'] = $attrib->type; + $metadata['attribute_types'][$attrib->name]['required'] = $attrib->required; + } + } + } + } + + // if not has a constructor will inject values into public properties + if (false === $metadata['has_constructor']) { + // collect all public properties + foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { + $metadata['properties'][$property->name] = $property->name; + + // checks if the property has @var annotation + if ((false !== $propertyComment = $property->getDocComment()) + && false !== strpos($propertyComment, '@var') + && preg_match('/@var\s+([^\s]+)/',$propertyComment, $matches)) { + // literal type declaration + $value = $matches[1]; + + // handle internal type declaration + $type = isset(self::$typeMap[$value]) ? self::$typeMap[$value] : $value; + + // handle the case if the property type is mixed + if ('mixed' !== $type) { + // Checks if the property has @var array annotation + if (false !== $pos = strpos($type, '<')) { + $arrayType = substr($type, $pos+1, -1); + $type = 'array'; + + if (isset(self::$typeMap[$arrayType])) { + $arrayType = self::$typeMap[$arrayType]; + } + + $metadata['attribute_types'][$property->name]['array_type'] = $arrayType; + } + + $metadata['attribute_types'][$property->name]['type'] = $type; + $metadata['attribute_types'][$property->name]['value'] = $value; + $metadata['attribute_types'][$property->name]['required'] = false !== strpos($propertyComment, '@Required'); + } + } + } + + // choose the first property as default property + $metadata['default_property'] = reset($metadata['properties']); + } + } + + self::$annotationMetadata[$name] = $metadata; + } + + /** + * Annotations ::= Annotation {[ "*" ]* [Annotation]}* + * + * @return array + */ + private function Annotations() + { + $annotations = array(); + + while (null !== $this->lexer->lookahead) { + if (DocLexer::T_AT !== $this->lexer->lookahead['type']) { + $this->lexer->moveNext(); + continue; + } + + // make sure the @ is preceded by non-catchable pattern + if (null !== $this->lexer->token && $this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen($this->lexer->token['value'])) { + $this->lexer->moveNext(); + continue; + } + + // make sure the @ is followed by either a namespace separator, or + // an identifier token + if ((null === $peek = $this->lexer->glimpse()) + || (DocLexer::T_NAMESPACE_SEPARATOR !== $peek['type'] && !in_array($peek['type'], self::$classIdentifiers, true)) + || $peek['position'] !== $this->lexer->lookahead['position'] + 1) { + $this->lexer->moveNext(); + continue; + } + + $this->isNestedAnnotation = false; + if (false !== $annot = $this->Annotation()) { + $annotations[] = $annot; + } + } + + return $annotations; + } + + /** + * Annotation ::= "@" AnnotationName ["(" [Values] ")"] + * AnnotationName ::= QualifiedName | SimpleName + * QualifiedName ::= NameSpacePart "\" {NameSpacePart "\"}* SimpleName + * NameSpacePart ::= identifier | null | false | true + * SimpleName ::= identifier | null | false | true + * + * @return mixed False if it is not a valid annotation. + */ + private function Annotation() + { + $this->match(DocLexer::T_AT); + + // check if we have an annotation + if ($this->lexer->isNextTokenAny(self::$classIdentifiers)) { + $this->lexer->moveNext(); + $name = $this->lexer->token['value']; + } else if ($this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR)) { + $name = ''; + } else { + $this->syntaxError('namespace separator or identifier'); + } + + while ($this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen($this->lexer->token['value']) && $this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR)) { + $this->match(DocLexer::T_NAMESPACE_SEPARATOR); + $this->matchAny(self::$classIdentifiers); + $name .= '\\'.$this->lexer->token['value']; + } + + // only process names which are not fully qualified, yet + // fully qualified names must start with a \ + $originalName = $name; + if ('\\' !== $name[0]) { + $alias = (false === $pos = strpos($name, '\\'))? $name : substr($name, 0, $pos); + + $found = false; + if ($this->namespaces) { + foreach ($this->namespaces as $namespace) { + if ($this->classExists($namespace.'\\'.$name)) { + $name = $namespace.'\\'.$name; + $found = true; + break; + } + } + } elseif (isset($this->imports[$loweredAlias = strtolower($alias)])) { + if (false !== $pos) { + $name = $this->imports[$loweredAlias].substr($name, $pos); + } else { + $name = $this->imports[$loweredAlias]; + } + $found = true; + } elseif (isset($this->imports['__NAMESPACE__']) && $this->classExists($this->imports['__NAMESPACE__'].'\\'.$name)) { + $name = $this->imports['__NAMESPACE__'].'\\'.$name; + $found = true; + } elseif ($this->classExists($name)) { + $found = true; + } + + if (!$found) { + if ($this->ignoreNotImportedAnnotations || isset($this->ignoredAnnotationNames[$name])) { + return false; + } + + throw AnnotationException::semanticalError(sprintf('The annotation "@%s" in %s was never imported. Did you maybe forget to add a "use" statement for this annotation?', $name, $this->context)); + } + } + + if (!$this->classExists($name)) { + throw AnnotationException::semanticalError(sprintf('The annotation "@%s" in %s does not exist, or could not be auto-loaded.', $name, $this->context)); + } + + // at this point, $name contains the fully qualified class name of the + // annotation, and it is also guaranteed that this class exists, and + // that it is loaded + + + // collects the metadata annotation only if there is not yet + if (!isset(self::$annotationMetadata[$name])) { + $this->collectAnnotationMetadata($name); + } + + // verify that the class is really meant to be an annotation and not just any ordinary class + if (self::$annotationMetadata[$name]['is_annotation'] === false) { + if (isset($this->ignoredAnnotationNames[$originalName])) { + return false; + } + + throw AnnotationException::semanticalError(sprintf('The class "%s" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "%s". If it is indeed no annotation, then you need to add @IgnoreAnnotation("%s") to the _class_ doc comment of %s.', $name, $name, $originalName, $this->context)); + } + + //if target is nested annotation + $target = $this->isNestedAnnotation ? Target::TARGET_ANNOTATION : $this->target; + + // Next will be nested + $this->isNestedAnnotation = true; + + //if annotation does not support current target + if (0 === (self::$annotationMetadata[$name]['targets'] & $target) && $target) { + throw AnnotationException::semanticalError( + sprintf('Annotation @%s is not allowed to be declared on %s. You may only use this annotation on these code elements: %s.', + $originalName, $this->context, self::$annotationMetadata[$name]['targets_literal']) + ); + } + + $values = array(); + if ($this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) { + $this->match(DocLexer::T_OPEN_PARENTHESIS); + + if ( ! $this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) { + $values = $this->Values(); + } + + $this->match(DocLexer::T_CLOSE_PARENTHESIS); + } + + // checks all declared attributes + foreach (self::$annotationMetadata[$name]['attribute_types'] as $property => $type) { + if ($property === self::$annotationMetadata[$name]['default_property'] + && !isset($values[$property]) && isset($values['value'])) { + $property = 'value'; + } + + // handle a not given attribute or null value + if (!isset($values[$property])) { + if ($type['required']) { + throw AnnotationException::requiredError($property, $originalName, $this->context, 'a(n) '.$type['value']); + } + + continue; + } + + if ($type['type'] === 'array') { + // handle the case of a single value + if (!is_array($values[$property])) { + $values[$property] = array($values[$property]); + } + + // checks if the attribute has array type declaration, such as "array" + if (isset($type['array_type'])) { + foreach ($values[$property] as $item) { + if (gettype($item) !== $type['array_type'] && !$item instanceof $type['array_type']) { + throw AnnotationException::typeError($property, $originalName, $this->context, 'either a(n) '.$type['array_type'].', or an array of '.$type['array_type'].'s', $item); + } + } + } + } elseif (gettype($values[$property]) !== $type['type'] && !$values[$property] instanceof $type['type']) { + throw AnnotationException::typeError($property, $originalName, $this->context, 'a(n) '.$type['value'], $values[$property]); + } + } + + // check if the annotation expects values via the constructor, + // or directly injected into public properties + if (self::$annotationMetadata[$name]['has_constructor'] === true) { + return new $name($values); + } + + $instance = new $name(); + foreach ($values as $property => $value) { + if (!isset(self::$annotationMetadata[$name]['properties'][$property])) { + if ('value' !== $property) { + throw AnnotationException::creationError(sprintf('The annotation @%s declared on %s does not have a property named "%s". Available properties: %s', $originalName, $this->context, $property, implode(', ', self::$annotationMetadata[$name]['properties']))); + } + + // handle the case if the property has no annotations + if (!$property = self::$annotationMetadata[$name]['default_property']) { + throw AnnotationException::creationError(sprintf('The annotation @%s declared on %s does not accept any values, but got %s.', $originalName, $this->context, json_encode($values))); + } + } + + $instance->{$property} = $value; + } + + return $instance; + } + + /** + * Values ::= Array | Value {"," Value}* + * + * @return array + */ + private function Values() + { + $values = array(); + + // Handle the case of a single array as value, i.e. @Foo({....}) + if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) { + $values['value'] = $this->Value(); + return $values; + } + + $values[] = $this->Value(); + + while ($this->lexer->isNextToken(DocLexer::T_COMMA)) { + $this->match(DocLexer::T_COMMA); + $token = $this->lexer->lookahead; + $value = $this->Value(); + + if ( ! is_object($value) && ! is_array($value)) { + $this->syntaxError('Value', $token); + } + + $values[] = $value; + } + + foreach ($values as $k => $value) { + if (is_object($value) && $value instanceof \stdClass) { + $values[$value->name] = $value->value; + } else if ( ! isset($values['value'])){ + $values['value'] = $value; + } else { + if ( ! is_array($values['value'])) { + $values['value'] = array($values['value']); + } + + $values['value'][] = $value; + } + + unset($values[$k]); + } + + return $values; + } + + /** + * Value ::= PlainValue | FieldAssignment + * + * @return mixed + */ + private function Value() + { + $peek = $this->lexer->glimpse(); + + if (DocLexer::T_EQUALS === $peek['type']) { + return $this->FieldAssignment(); + } + + return $this->PlainValue(); + } + + /** + * PlainValue ::= integer | string | float | boolean | Array | Annotation + * + * @return mixed + */ + private function PlainValue() + { + if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) { + return $this->Arrayx(); + } + + if ($this->lexer->isNextToken(DocLexer::T_AT)) { + return $this->Annotation(); + } + + switch ($this->lexer->lookahead['type']) { + case DocLexer::T_STRING: + $this->match(DocLexer::T_STRING); + return $this->lexer->token['value']; + + case DocLexer::T_INTEGER: + $this->match(DocLexer::T_INTEGER); + return (int)$this->lexer->token['value']; + + case DocLexer::T_FLOAT: + $this->match(DocLexer::T_FLOAT); + return (float)$this->lexer->token['value']; + + case DocLexer::T_TRUE: + $this->match(DocLexer::T_TRUE); + return true; + + case DocLexer::T_FALSE: + $this->match(DocLexer::T_FALSE); + return false; + + case DocLexer::T_NULL: + $this->match(DocLexer::T_NULL); + return null; + + default: + $this->syntaxError('PlainValue'); + } + } + + /** + * FieldAssignment ::= FieldName "=" PlainValue + * FieldName ::= identifier + * + * @return array + */ + private function FieldAssignment() + { + $this->match(DocLexer::T_IDENTIFIER); + $fieldName = $this->lexer->token['value']; + + $this->match(DocLexer::T_EQUALS); + + $item = new \stdClass(); + $item->name = $fieldName; + $item->value = $this->PlainValue(); + + return $item; + } + + /** + * Array ::= "{" ArrayEntry {"," ArrayEntry}* [","] "}" + * + * @return array + */ + private function Arrayx() + { + $array = $values = array(); + + $this->match(DocLexer::T_OPEN_CURLY_BRACES); + $values[] = $this->ArrayEntry(); + + while ($this->lexer->isNextToken(DocLexer::T_COMMA)) { + $this->match(DocLexer::T_COMMA); + + // optional trailing comma + if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) { + break; + } + + $values[] = $this->ArrayEntry(); + } + + $this->match(DocLexer::T_CLOSE_CURLY_BRACES); + + foreach ($values as $value) { + list ($key, $val) = $value; + + if ($key !== null) { + $array[$key] = $val; + } else { + $array[] = $val; + } + } + + return $array; + } + + /** + * ArrayEntry ::= Value | KeyValuePair + * KeyValuePair ::= Key ("=" | ":") PlainValue + * Key ::= string | integer + * + * @return array + */ + private function ArrayEntry() + { + $peek = $this->lexer->glimpse(); + + if (DocLexer::T_EQUALS === $peek['type'] + || DocLexer::T_COLON === $peek['type']) { + $this->matchAny(array(DocLexer::T_INTEGER, DocLexer::T_STRING)); + + $key = $this->lexer->token['value']; + $this->matchAny(array(DocLexer::T_EQUALS, DocLexer::T_COLON)); + + return array($key, $this->PlainValue()); + } + + return array(null, $this->Value()); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/FileCacheReader.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/FileCacheReader.php new file mode 100644 index 0000000..4a42b58 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/FileCacheReader.php @@ -0,0 +1,209 @@ +. + */ + +namespace Doctrine\Common\Annotations; + + +/** + * File cache reader for annotations. + * + * @author Johannes M. Schmitt + * @author Benjamin Eberlei + */ +class FileCacheReader implements Reader +{ + /** + * @var Reader + */ + private $reader; + private $dir; + private $debug; + private $loadedAnnotations = array(); + + public function __construct(Reader $reader, $cacheDir, $debug = false) + { + $this->reader = $reader; + if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $cacheDir)); + } + if (!is_writable($cacheDir)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" is not writable. Both, the webserver and the console user need access. You can manage access rights for multiple users with "chmod +a". If your system does not support this, check out the acl package.', $cacheDir)); + } + + $this->dir = rtrim($cacheDir, '\\/'); + $this->debug = $debug; + } + + public function getClassAnnotations(\ReflectionClass $class) + { + $key = $class->getName(); + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!file_exists($path)) { + $annot = $this->reader->getClassAnnotations($class); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + if ($this->debug + && (false !== $filename = $class->getFilename()) + && filemtime($path) < filemtime($filename)) { + @unlink($path); + + $annot = $this->reader->getClassAnnotations($class); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + return $this->loadedAnnotations[$key] = include $path; + } + + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $key = $class->getName().'$'.$property->getName(); + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!file_exists($path)) { + $annot = $this->reader->getPropertyAnnotations($property); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + if ($this->debug + && (false !== $filename = $class->getFilename()) + && filemtime($path) < filemtime($filename)) { + unlink($path); + + $annot = $this->reader->getPropertyAnnotations($property); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + return $this->loadedAnnotations[$key] = include $path; + } + + public function getMethodAnnotations(\ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $key = $class->getName().'#'.$method->getName(); + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!file_exists($path)) { + $annot = $this->reader->getMethodAnnotations($method); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + if ($this->debug + && (false !== $filename = $class->getFilename()) + && filemtime($path) < filemtime($filename)) { + unlink($path); + + $annot = $this->reader->getMethodAnnotations($method); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + return $this->loadedAnnotations[$key] = include $path; + } + + private function saveCacheFile($path, $data) + { + file_put_contents($path, 'getClassAnnotations($class); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Gets a method annotation. + * + * @param ReflectionMethod $method + * @param string $annotationName The name of the annotation. + * @return The Annotation or NULL, if the requested annotation does not exist. + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + $annotations = $this->getMethodAnnotations($method); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Gets a property annotation. + * + * @param ReflectionProperty $property + * @param string $annotationName The name of the annotation. + * @return The Annotation or NULL, if the requested annotation does not exist. + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + $annotations = $this->getPropertyAnnotations($property); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + public function clearLoadedAnnotations() + { + $this->loadedAnnotations = array(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/IndexedReader.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/IndexedReader.php new file mode 100644 index 0000000..1eea492 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/IndexedReader.php @@ -0,0 +1,94 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Reader; + +/** + * Allows the reader to be used in-place of Doctrine's reader. + * + * @author Johannes M. Schmitt + */ +class IndexedReader implements Reader +{ + private $delegate; + + public function __construct(Reader $reader) + { + $this->delegate = $reader; + } + + public function getClassAnnotations(\ReflectionClass $class) + { + $annotations = array(); + foreach ($this->delegate->getClassAnnotations($class) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + public function getClassAnnotation(\ReflectionClass $class, $annotation) + { + return $this->delegate->getClassAnnotation($class, $annotation); + } + + public function getMethodAnnotations(\ReflectionMethod $method) + { + $annotations = array(); + foreach ($this->delegate->getMethodAnnotations($method) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + public function getMethodAnnotation(\ReflectionMethod $method, $annotation) + { + return $this->delegate->getMethodAnnotation($method, $annotation); + } + + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $annotations = array(); + foreach ($this->delegate->getPropertyAnnotations($property) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + public function getPropertyAnnotation(\ReflectionProperty $property, $annotation) + { + return $this->delegate->getPropertyAnnotation($property, $annotation); + } + + /** + * Proxy all methods to the delegate. + * + * @param type $method + * @param type $args + * @return type + */ + public function __call($method, $args) + { + return call_user_func_array(array($this->delegate, $method), $args); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/PhpParser.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/PhpParser.php new file mode 100644 index 0000000..a14f8f5 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/PhpParser.php @@ -0,0 +1,203 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use SplFileObject; + +/** + * Parses a file for namespaces/use/class declarations. + * + * @author Fabien Potencier + * @author Christian Kaps + */ +final class PhpParser +{ + /** + * The token list. + * + * @var array + */ + private $tokens; + + /** + * The number of tokens. + * + * @var int + */ + private $numTokens = 0; + + /** + * The current array pointer. + * + * @var int + */ + private $pointer = 0; + + /** + * Parses a class. + * + * @param \ReflectionClass $class A ReflectionClass object. + * @return array A list with use statements in the form (Alias => FQN). + */ + public function parseClass(\ReflectionClass $class) + { + if (false === $filename = $class->getFilename()) { + return array(); + } + + $content = $this->getFileContent($filename, $class->getStartLine()); + $namespace = str_replace('\\', '\\\\', $class->getNamespaceName()); + $content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content); + $this->tokens = token_get_all('numTokens = count($this->tokens); + $this->pointer = 0; + + $statements = $this->parseUseStatements($class->getNamespaceName()); + + return $statements; + } + + /** + * Get the content of the file right up to the given line number. + * + * @param string $filename The name of the file to load. + * @param int $lineNumber The number of lines to read from file. + * @return string The content of the file. + */ + private function getFileContent($filename, $lineNumber) + { + $content = ''; + $lineCnt = 0; + $file = new SplFileObject($filename); + while(!$file->eof()) { + if ($lineCnt++ == $lineNumber) { + break; + } + + $content .= $file->fgets(); + } + + return $content; + } + + /** + * Gets the next non whitespace and non comment token. + * + * @return array The token if exists, null otherwise. + */ + private function next() + { + for ($i = $this->pointer; $i < $this->numTokens; $i++) { + $this->pointer++; + if ($this->tokens[$i][0] === T_WHITESPACE || + $this->tokens[$i][0] === T_COMMENT || + $this->tokens[$i][0] === T_DOC_COMMENT) { + + continue; + } + + return $this->tokens[$i]; + } + + return null; + } + + /** + * Get all use statements. + * + * @param string $namespaceName The namespace name of the reflected class. + * @return array A list with all found use statements. + */ + private function parseUseStatements($namespaceName) + { + $statements = array(); + while (($token = $this->next())) { + if ($token[0] === T_USE) { + $statements = array_merge($statements, $this->parseUseStatement()); + continue; + } else if ($token[0] !== T_NAMESPACE || $this->parseNamespace() != $namespaceName) { + continue; + } + + // Get fresh array for new namespace. This is to prevent the parser to collect the use statements + // for a previous namespace with the same name. This is the case if a namespace is defined twice + // or if a namespace with the same name is commented out. + $statements = array(); + } + + return $statements; + } + + /** + * Get the namespace name. + * + * @return string The found namespace name. + */ + private function parseNamespace() + { + $namespace = ''; + while (($token = $this->next())){ + if ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR) { + $namespace .= $token[1]; + } else { + break; + } + } + + return $namespace; + } + + /** + * Parse a single use statement. + * + * @return array A list with all found class names for a use statement. + */ + private function parseUseStatement() + { + $class = ''; + $alias = ''; + $statements = array(); + $explicitAlias = false; + while (($token = $this->next())) { + $isNameToken = $token[0] === T_STRING || $token[0] === T_NS_SEPARATOR; + if (!$explicitAlias && $isNameToken) { + $class .= $token[1]; + $alias = $token[1]; + } else if ($explicitAlias && $isNameToken) { + $alias .= $token[1]; + } else if ($token[0] === T_AS) { + $explicitAlias = true; + $alias = ''; + } else if ($token === ',') { + $statements[strtolower($alias)] = $class; + $class = ''; + $alias = ''; + $explicitAlias = false; + } else if ($token === ';') { + $statements[strtolower($alias)] = $class; + break; + } else { + break; + } + } + + return $statements; + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Reader.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Reader.php new file mode 100644 index 0000000..8e85d39 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/Reader.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Interface for annotation readers. + * + * @author Johannes M. Schmitt + */ +interface Reader +{ + function getClassAnnotations(\ReflectionClass $class); + function getClassAnnotation(\ReflectionClass $class, $annotationName); + function getMethodAnnotations(\ReflectionMethod $method); + function getMethodAnnotation(\ReflectionMethod $method, $annotationName); + function getPropertyAnnotations(\ReflectionProperty $property); + function getPropertyAnnotation(\ReflectionProperty $property, $annotationName); +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php new file mode 100644 index 0000000..a13c7fa --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php @@ -0,0 +1,152 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Annotation\Target; + +/** + * Simple Annotation Reader. + * + * This annotation reader is intended to be used in projects where you have + * full-control over all annotations that are available. + * + * @since 2.2 + * @author Johannes M. Schmitt + * @author Fabio B. Silva + */ +class SimpleAnnotationReader implements Reader +{ + /** + * @var DocParser + */ + private $parser; + + /** + * Constructor. + * + * Initializes a new SimpleAnnotationReader. + */ + public function __construct() + { + $this->parser = new DocParser(); + $this->parser->setIgnoreNotImportedAnnotations(true); + } + + /** + * Adds a namespace in which we will look for annotations. + * + * @param string $namespace + */ + public function addNamespace($namespace) + { + $this->parser->addNamespace($namespace); + } + + /** + * Gets the annotations applied to a class. + * + * @param ReflectionClass $class The ReflectionClass of the class from which + * the class annotations should be read. + * @return array An array of Annotations. + */ + public function getClassAnnotations(\ReflectionClass $class) + { + return $this->parser->parse($class->getDocComment(), 'class '.$class->getName()); + } + + /** + * Gets the annotations applied to a method. + * + * @param ReflectionMethod $property The ReflectionMethod of the method from which + * the annotations should be read. + * @return array An array of Annotations. + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + return $this->parser->parse($method->getDocComment(), 'method '.$method->getDeclaringClass()->name.'::'.$method->getName().'()'); + } + + /** + * Gets the annotations applied to a property. + * + * @param ReflectionProperty $property The ReflectionProperty of the property + * from which the annotations should be read. + * @return array An array of Annotations. + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + return $this->parser->parse($property->getDocComment(), 'property '.$property->getDeclaringClass()->name.'::$'.$property->getName()); + } + + /** + * Gets a class annotation. + * + * @param ReflectionClass $class The ReflectionClass of the class from which + * the class annotations should be read. + * @param string $annotationName The name of the annotation. + * @return The Annotation or NULL, if the requested annotation does not exist. + */ + public function getClassAnnotation(\ReflectionClass $class, $annotationName) + { + foreach ($this->getClassAnnotations($class) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Gets a method annotation. + * + * @param ReflectionMethod $method + * @param string $annotationName The name of the annotation. + * @return The Annotation or NULL, if the requested annotation does not exist. + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + foreach ($this->getMethodAnnotations($method) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Gets a property annotation. + * + * @param ReflectionProperty $property + * @param string $annotationName The name of the annotation. + * @return The Annotation or NULL, if the requested annotation does not exist. + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + foreach ($this->getPropertyAnnotations($property) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/ApcCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/ApcCache.php new file mode 100644 index 0000000..a59296f --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/ApcCache.php @@ -0,0 +1,97 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * APC cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class ApcCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return apc_fetch($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $found = false; + + apc_fetch($id, $found); + + return $found; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return (bool) apc_store($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return apc_delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return apc_clear_cache() && apc_clear_cache('user'); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = apc_cache_info(); + $sma = apc_sma_info(); + + return array( + Cache::STATS_HITS => $info['num_hits'], + Cache::STATS_MISSES => $info['num_misses'], + Cache::STATS_UPTIME => $info['start_time'], + Cache::STATS_MEMORY_USAGE => $info['mem_size'], + Cache::STATS_MEMORY_AVAILIABLE => $sma['avail_mem'], + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/ArrayCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/ArrayCache.php new file mode 100644 index 0000000..8a0b982 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/ArrayCache.php @@ -0,0 +1,96 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Array cache driver. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class ArrayCache extends CacheProvider +{ + /** + * @var array $data + */ + private $data = array(); + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return (isset($this->data[$id])) ? $this->data[$id] : false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return isset($this->data[$id]); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $this->data[$id] = $data; + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + unset($this->data[$id]); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $this->data = array(); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + return null; + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/Cache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/Cache.php new file mode 100644 index 0000000..d303bde --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/Cache.php @@ -0,0 +1,102 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Interface for cache drivers. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Fabio B. Silva + */ +interface Cache +{ + const STATS_HITS = 'hits'; + const STATS_MISSES = 'misses'; + const STATS_UPTIME = 'uptime'; + const STATS_MEMORY_USAGE = 'memory_usage'; + const STATS_MEMORY_AVAILIABLE = 'memory_available'; + + /** + * Fetches an entry from the cache. + * + * @param string $id cache id The id of the cache entry to fetch. + * @return string The cached data or FALSE, if no cache entry exists for the given id. + */ + function fetch($id); + + /** + * Test if an entry exists in the cache. + * + * @param string $id cache id The cache id of the entry to check for. + * @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise. + */ + function contains($id); + + /** + * Puts data into the cache. + * + * @param string $id The cache id. + * @param string $data The cache entry/data. + * @param int $lifeTime The lifetime. If != 0, sets a specific lifetime for this cache entry (0 => infinite lifeTime). + * @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise. + */ + function save($id, $data, $lifeTime = 0); + + /** + * Deletes a cache entry. + * + * @param string $id cache id + * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + function delete($id); + + /** + * Retrieves cached information from data store + * + * The server's statistics array has the following values: + * + * - hits + * Number of keys that have been requested and found present. + * + * - misses + * Number of items that have been requested and not found. + * + * - uptime + * Time that the server is running. + * + * - memory_usage + * Memory used by this server to store items. + * + * - memory_available + * Memory allowed to use for storage. + * + * @since 2.2 + * @var array Associative array with server's statistics if available, NULL otherwise. + */ + function getStats(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/CacheProvider.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/CacheProvider.php new file mode 100644 index 0000000..fa11fc2 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/CacheProvider.php @@ -0,0 +1,188 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Base class for cache provider implementations. + * + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Fabio B. Silva + */ +abstract class CacheProvider implements Cache +{ + const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]'; + + /** + * @var string The namespace to prefix all cache ids with + */ + private $namespace = ''; + + /** + * Set the namespace to prefix all cache ids with. + * + * @param string $namespace + * @return void + */ + public function setNamespace($namespace) + { + $this->namespace = (string) $namespace; + } + + /** + * Retrieve the namespace that prefixes all cache ids. + * + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * {@inheritdoc} + */ + public function fetch($id) + { + return $this->doFetch($this->getNamespacedId($id)); + } + + /** + * {@inheritdoc} + */ + public function contains($id) + { + return $this->doContains($this->getNamespacedId($id)); + } + + /** + * {@inheritdoc} + */ + public function save($id, $data, $lifeTime = 0) + { + return $this->doSave($this->getNamespacedId($id), $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + public function delete($id) + { + return $this->doDelete($this->getNamespacedId($id)); + } + + /** + * {@inheritdoc} + */ + public function getStats() + { + return $this->doGetStats(); + } + + /** + * Deletes all cache entries. + * + * @return boolean TRUE if the cache entries were successfully flushed, FALSE otherwise. + */ + public function flushAll() + { + return $this->doFlush(); + } + + /** + * Delete all cache entries. + * + * @return boolean TRUE if the cache entries were successfully deleted, FALSE otherwise. + */ + public function deleteAll() + { + $namespaceCacheKey = sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); + $namespaceVersion = ($this->doContains($namespaceCacheKey)) ? $this->doFetch($namespaceCacheKey) : 1; + + return $this->doSave($namespaceCacheKey, $namespaceVersion + 1); + } + + /** + * Prefix the passed id with the configured namespace value + * + * @param string $id The id to namespace + * @return string $id The namespaced id + */ + private function getNamespacedId($id) + { + $namespaceCacheKey = sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); + $namespaceVersion = ($this->doContains($namespaceCacheKey)) ? $this->doFetch($namespaceCacheKey) : 1; + + return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion); + } + + /** + * Fetches an entry from the cache. + * + * @param string $id cache id The id of the cache entry to fetch. + * @return string The cached data or FALSE, if no cache entry exists for the given id. + */ + abstract protected function doFetch($id); + + /** + * Test if an entry exists in the cache. + * + * @param string $id cache id The cache id of the entry to check for. + * @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise. + */ + abstract protected function doContains($id); + + /** + * Puts data into the cache. + * + * @param string $id The cache id. + * @param string $data The cache entry/data. + * @param int $lifeTime The lifetime. If != false, sets a specific lifetime for this cache entry (null => infinite lifeTime). + * @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise. + */ + abstract protected function doSave($id, $data, $lifeTime = false); + + /** + * Deletes a cache entry. + * + * @param string $id cache id + * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + abstract protected function doDelete($id); + + /** + * Deletes all cache entries. + * + * @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + abstract protected function doFlush(); + + /** + * Retrieves cached information from data store + * + * @since 2.2 + * @return array An associative array with server's statistics if available, NULL otherwise. + */ + abstract protected function doGetStats(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/MemcacheCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/MemcacheCache.php new file mode 100644 index 0000000..dd6d1e3 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/MemcacheCache.php @@ -0,0 +1,121 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Memcache; + +/** + * Memcache cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class MemcacheCache extends CacheProvider +{ + /** + * @var Memcache + */ + private $memcache; + + /** + * Sets the memcache instance to use. + * + * @param Memcache $memcache + */ + public function setMemcache(Memcache $memcache) + { + $this->memcache = $memcache; + } + + /** + * Gets the memcache instance used by the cache. + * + * @return Memcache + */ + public function getMemcache() + { + return $this->memcache; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->memcache->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (bool) $this->memcache->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 30 * 24 * 3600) { + $lifeTime = time() + $lifeTime; + } + return $this->memcache->set($id, $data, 0, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->memcache->delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->memcache->flush(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $stats = $this->memcache->getStats(); + return array( + Cache::STATS_HITS => $stats['get_hits'], + Cache::STATS_MISSES => $stats['get_misses'], + Cache::STATS_UPTIME => $stats['uptime'], + Cache::STATS_MEMORY_USAGE => $stats['bytes'], + Cache::STATS_MEMORY_AVAILIABLE => $stats['limit_maxbytes'], + ); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/MemcachedCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/MemcachedCache.php new file mode 100644 index 0000000..4675fae --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/MemcachedCache.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Memcached; + +/** + * Memcached cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class MemcachedCache extends CacheProvider +{ + /** + * @var Memcached + */ + private $memcached; + + /** + * Sets the memcache instance to use. + * + * @param Memcached $memcached + */ + public function setMemcached(Memcached $memcached) + { + $this->memcached = $memcached; + } + + /** + * Gets the memcached instance used by the cache. + * + * @return Memcached + */ + public function getMemcached() + { + return $this->memcached; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->memcached->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (false !== $this->memcached->get($id)); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 30 * 24 * 3600) { + $lifeTime = time() + $lifeTime; + } + return $this->memcached->set($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->memcached->delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->memcached->flush(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $stats = $this->memcached->getStats(); + $servers = $this->memcached->getServerList(); + $key = $servers[0]['host'] . ':' . $servers[0]['port']; + $stats = $stats[$key]; + return array( + Cache::STATS_HITS => $stats['get_hits'], + Cache::STATS_MISSES => $stats['get_misses'], + Cache::STATS_UPTIME => $stats['uptime'], + Cache::STATS_MEMORY_USAGE => $stats['bytes'], + Cache::STATS_MEMORY_AVAILIABLE => $stats['limit_maxbytes'], + ); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/WinCacheCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/WinCacheCache.php new file mode 100644 index 0000000..ed8ca74 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/WinCacheCache.php @@ -0,0 +1,92 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * WinCache cache provider. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class WincacheCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return wincache_ucache_get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return wincache_ucache_exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return (bool) wincache_ucache_set($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return wincache_ucache_delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return wincache_ucache_clear(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = wincache_ucache_info(); + $meminfo= wincache_ucache_meminfo(); + return array( + Cache::STATS_HITS => $info['total_hit_count'], + Cache::STATS_MISSES => $info['total_miss_count'], + Cache::STATS_UPTIME => $info['total_cache_uptime'], + Cache::STATS_MEMORY_USAGE => $meminfo['memory_total'], + Cache::STATS_MEMORY_AVAILIABLE => $meminfo['memory_free'], + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/XcacheCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/XcacheCache.php new file mode 100644 index 0000000..6e22d26 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/XcacheCache.php @@ -0,0 +1,110 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Xcache cache driver. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class XcacheCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->doContains($id) ? unserialize(xcache_get($id)) : false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return xcache_isset($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return xcache_set($id, serialize($data), (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return xcache_unset($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $this->checkAuthorization(); + + xcache_clear_cache(XC_TYPE_VAR, 0); + + return true; + } + + /** + * Checks that xcache.admin.enable_auth is Off + * + * @throws \BadMethodCallException When xcache.admin.enable_auth is On + * @return void + */ + protected function checkAuthorization() + { + if (ini_get('xcache.admin.enable_auth')) { + throw new \BadMethodCallException('To use all features of \Doctrine\Common\Cache\XcacheCache, you must set "xcache.admin.enable_auth" to "Off" in your php.ini.'); + } + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $this->checkAuthorization(); + + $info = xcache_info(XC_TYPE_VAR, 0); + return array( + Cache::STATS_HITS => $info['hits'], + Cache::STATS_MISSES => $info['misses'], + Cache::STATS_UPTIME => null, + Cache::STATS_MEMORY_USAGE => $info['size'], + Cache::STATS_MEMORY_AVAILIABLE => $info['avail'], + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Cache/ZendDataCache.php b/vendor/doctrine/common/lib/Doctrine/Common/Cache/ZendDataCache.php new file mode 100644 index 0000000..4e4dabe --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Cache/ZendDataCache.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Zend Data Cache cache driver. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Ralph Schindler + * @author Guilherme Blanco + */ +class ZendDataCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return zend_shm_cache_fetch($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (false !== zend_shm_cache_fetch($id)); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return zend_shm_cache_store($id, $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return zend_shm_cache_delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $namespace = $this->getNamespace(); + if (empty($namespace)) { + return zend_shm_cache_clear(); + } + return zend_shm_cache_clear($namespace); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + return null; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php b/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php new file mode 100644 index 0000000..375b0d6 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php @@ -0,0 +1,262 @@ +. + */ + +namespace Doctrine\Common; + +/** + * A ClassLoader is an autoloader for class files that can be + * installed on the SPL autoload stack. It is a class loader that either loads only classes + * of a specific namespace or all namespaces and it is suitable for working together + * with other autoloaders in the SPL autoload stack. + * + * If no include path is configured through the constructor or {@link setIncludePath}, a ClassLoader + * relies on the PHP include_path. + * + * @author Roman Borschel + * @since 2.0 + */ +class ClassLoader +{ + /** + * @var string PHP file extension + */ + protected $fileExtension = '.php'; + + /** + * @var string Current namespace + */ + protected $namespace; + + /** + * @var string Current include path + */ + protected $includePath; + + /** + * @var string PHP namespace separator + */ + protected $namespaceSeparator = '\\'; + + /** + * Creates a new ClassLoader that loads classes of the + * specified namespace from the specified include path. + * + * If no include path is given, the ClassLoader relies on the PHP include_path. + * If neither a namespace nor an include path is given, the ClassLoader will + * be responsible for loading all classes, thereby relying on the PHP include_path. + * + * @param string $ns The namespace of the classes to load. + * @param string $includePath The base include path to use. + */ + public function __construct($ns = null, $includePath = null) + { + $this->namespace = $ns; + $this->includePath = $includePath; + } + + /** + * Sets the namespace separator used by classes in the namespace of this ClassLoader. + * + * @param string $sep The separator to use. + */ + public function setNamespaceSeparator($sep) + { + $this->namespaceSeparator = $sep; + } + + /** + * Gets the namespace separator used by classes in the namespace of this ClassLoader. + * + * @return string + */ + public function getNamespaceSeparator() + { + return $this->namespaceSeparator; + } + + /** + * Sets the base include path for all class files in the namespace of this ClassLoader. + * + * @param string $includePath + */ + public function setIncludePath($includePath) + { + $this->includePath = $includePath; + } + + /** + * Gets the base include path for all class files in the namespace of this ClassLoader. + * + * @return string + */ + public function getIncludePath() + { + return $this->includePath; + } + + /** + * Sets the file extension of class files in the namespace of this ClassLoader. + * + * @param string $fileExtension + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * Gets the file extension of class files in the namespace of this ClassLoader. + * + * @return string + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Registers this ClassLoader on the SPL autoload stack. + */ + public function register() + { + spl_autoload_register(array($this, 'loadClass')); + } + + /** + * Removes this ClassLoader from the SPL autoload stack. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $classname The name of the class to load. + * @return boolean TRUE if the class has been successfully loaded, FALSE otherwise. + */ + public function loadClass($className) + { + if ($this->namespace !== null && strpos($className, $this->namespace.$this->namespaceSeparator) !== 0) { + return false; + } + + require ($this->includePath !== null ? $this->includePath . DIRECTORY_SEPARATOR : '') + . str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) + . $this->fileExtension; + + return true; + } + + /** + * Asks this ClassLoader whether it can potentially load the class (file) with + * the given name. + * + * @param string $className The fully-qualified name of the class. + * @return boolean TRUE if this ClassLoader can load the class, FALSE otherwise. + */ + public function canLoadClass($className) + { + if ($this->namespace !== null && strpos($className, $this->namespace.$this->namespaceSeparator) !== 0) { + return false; + } + + $file = str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) . $this->fileExtension; + + if ($this->includePath !== null) { + return file_exists($this->includePath . DIRECTORY_SEPARATOR . $file); + } + + return (false !== stream_resolve_include_path($file)); + } + + /** + * Checks whether a class with a given name exists. A class "exists" if it is either + * already defined in the current request or if there is an autoloader on the SPL + * autoload stack that is a) responsible for the class in question and b) is able to + * load a class file in which the class definition resides. + * + * If the class is not already defined, each autoloader in the SPL autoload stack + * is asked whether it is able to tell if the class exists. If the autoloader is + * a ClassLoader, {@link canLoadClass} is used, otherwise the autoload + * function of the autoloader is invoked and expected to return a value that + * evaluates to TRUE if the class (file) exists. As soon as one autoloader reports + * that the class exists, TRUE is returned. + * + * Note that, depending on what kinds of autoloaders are installed on the SPL + * autoload stack, the class (file) might already be loaded as a result of checking + * for its existence. This is not the case with a ClassLoader, who separates + * these responsibilities. + * + * @param string $className The fully-qualified name of the class. + * @return boolean TRUE if the class exists as per the definition given above, FALSE otherwise. + */ + public static function classExists($className) + { + if (class_exists($className, false) || interface_exists($className, false)) { + return true; + } + + foreach (spl_autoload_functions() as $loader) { + if (is_array($loader)) { // array(???, ???) + if (is_object($loader[0])) { + if ($loader[0] instanceof ClassLoader) { // array($obj, 'methodName') + if ($loader[0]->canLoadClass($className)) { + return true; + } + } else if ($loader[0]->{$loader[1]}($className)) { + return true; + } + } else if ($loader[0]::$loader[1]($className)) { // array('ClassName', 'methodName') + return true; + } + } else if ($loader instanceof \Closure) { // function($className) {..} + if ($loader($className)) { + return true; + } + } else if (is_string($loader) && $loader($className)) { // "MyClass::loadClass" + return true; + } + } + + return class_exists($className, false) || interface_exists($className, false); + } + + /** + * Gets the ClassLoader from the SPL autoload stack that is responsible + * for (and is able to load) the class with the given name. + * + * @param string $className The name of the class. + * @return The ClassLoader for the class or NULL if no such ClassLoader exists. + */ + public static function getClassLoader($className) + { + foreach (spl_autoload_functions() as $loader) { + if (is_array($loader) + && $loader[0] instanceof ClassLoader + && $loader[0]->canLoadClass($className) + ) { + return $loader[0]; + } + } + + return null; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Collections/ArrayCollection.php b/vendor/doctrine/common/lib/Doctrine/Common/Collections/ArrayCollection.php new file mode 100644 index 0000000..2a7d4ea --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Collections/ArrayCollection.php @@ -0,0 +1,447 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Closure, ArrayIterator; + +/** + * An ArrayCollection is a Collection implementation that wraps a regular PHP array. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArrayCollection implements Collection +{ + /** + * An array containing the entries of this collection. + * + * @var array + */ + private $_elements; + + /** + * Initializes a new ArrayCollection. + * + * @param array $elements + */ + public function __construct(array $elements = array()) + { + $this->_elements = $elements; + } + + /** + * Gets the PHP array representation of this collection. + * + * @return array The PHP array representation of this collection. + */ + public function toArray() + { + return $this->_elements; + } + + /** + * Sets the internal iterator to the first element in the collection and + * returns this element. + * + * @return mixed + */ + public function first() + { + return reset($this->_elements); + } + + /** + * Sets the internal iterator to the last element in the collection and + * returns this element. + * + * @return mixed + */ + public function last() + { + return end($this->_elements); + } + + /** + * Gets the current key/index at the current internal iterator position. + * + * @return mixed + */ + public function key() + { + return key($this->_elements); + } + + /** + * Moves the internal iterator position to the next element. + * + * @return mixed + */ + public function next() + { + return next($this->_elements); + } + + /** + * Gets the element of the collection at the current internal iterator position. + * + * @return mixed + */ + public function current() + { + return current($this->_elements); + } + + /** + * Removes an element with a specific key/index from the collection. + * + * @param mixed $key + * @return mixed The removed element or NULL, if no element exists for the given key. + */ + public function remove($key) + { + if (isset($this->_elements[$key])) { + $removed = $this->_elements[$key]; + unset($this->_elements[$key]); + + return $removed; + } + + return null; + } + + /** + * Removes the specified element from the collection, if it is found. + * + * @param mixed $element The element to remove. + * @return boolean TRUE if this collection contained the specified element, FALSE otherwise. + */ + public function removeElement($element) + { + $key = array_search($element, $this->_elements, true); + + if ($key !== false) { + unset($this->_elements[$key]); + + return true; + } + + return false; + } + + /** + * ArrayAccess implementation of offsetExists() + * + * @see containsKey() + */ + public function offsetExists($offset) + { + return $this->containsKey($offset); + } + + /** + * ArrayAccess implementation of offsetGet() + * + * @see get() + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * ArrayAccess implementation of offsetGet() + * + * @see add() + * @see set() + */ + public function offsetSet($offset, $value) + { + if ( ! isset($offset)) { + return $this->add($value); + } + return $this->set($offset, $value); + } + + /** + * ArrayAccess implementation of offsetUnset() + * + * @see remove() + */ + public function offsetUnset($offset) + { + return $this->remove($offset); + } + + /** + * Checks whether the collection contains a specific key/index. + * + * @param mixed $key The key to check for. + * @return boolean TRUE if the given key/index exists, FALSE otherwise. + */ + public function containsKey($key) + { + return isset($this->_elements[$key]); + } + + /** + * Checks whether the given element is contained in the collection. + * Only element values are compared, not keys. The comparison of two elements + * is strict, that means not only the value but also the type must match. + * For objects this means reference equality. + * + * @param mixed $element + * @return boolean TRUE if the given element is contained in the collection, + * FALSE otherwise. + */ + public function contains($element) + { + foreach ($this->_elements as $collectionElement) { + if ($element === $collectionElement) { + return true; + } + } + + return false; + } + + /** + * Tests for the existance of an element that satisfies the given predicate. + * + * @param Closure $p The predicate. + * @return boolean TRUE if the predicate is TRUE for at least one element, FALSE otherwise. + */ + public function exists(Closure $p) + { + foreach ($this->_elements as $key => $element) { + if ($p($key, $element)) { + return true; + } + } + return false; + } + + /** + * Searches for a given element and, if found, returns the corresponding key/index + * of that element. The comparison of two elements is strict, that means not + * only the value but also the type must match. + * For objects this means reference equality. + * + * @param mixed $element The element to search for. + * @return mixed The key/index of the element or FALSE if the element was not found. + */ + public function indexOf($element) + { + return array_search($element, $this->_elements, true); + } + + /** + * Gets the element with the given key/index. + * + * @param mixed $key The key. + * @return mixed The element or NULL, if no element exists for the given key. + */ + public function get($key) + { + if (isset($this->_elements[$key])) { + return $this->_elements[$key]; + } + return null; + } + + /** + * Gets all keys/indexes of the collection elements. + * + * @return array + */ + public function getKeys() + { + return array_keys($this->_elements); + } + + /** + * Gets all elements. + * + * @return array + */ + public function getValues() + { + return array_values($this->_elements); + } + + /** + * Returns the number of elements in the collection. + * + * Implementation of the Countable interface. + * + * @return integer The number of elements in the collection. + */ + public function count() + { + return count($this->_elements); + } + + /** + * Adds/sets an element in the collection at the index / with the specified key. + * + * When the collection is a Map this is like put(key,value)/add(key,value). + * When the collection is a List this is like add(position,value). + * + * @param mixed $key + * @param mixed $value + */ + public function set($key, $value) + { + $this->_elements[$key] = $value; + } + + /** + * Adds an element to the collection. + * + * @param mixed $value + * @return boolean Always TRUE. + */ + public function add($value) + { + $this->_elements[] = $value; + return true; + } + + /** + * Checks whether the collection is empty. + * + * Note: This is preferrable over count() == 0. + * + * @return boolean TRUE if the collection is empty, FALSE otherwise. + */ + public function isEmpty() + { + return ! $this->_elements; + } + + /** + * Gets an iterator for iterating over the elements in the collection. + * + * @return ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->_elements); + } + + /** + * Applies the given function to each element in the collection and returns + * a new collection with the elements returned by the function. + * + * @param Closure $func + * @return Collection + */ + public function map(Closure $func) + { + return new static(array_map($func, $this->_elements)); + } + + /** + * Returns all the elements of this collection that satisfy the predicate p. + * The order of the elements is preserved. + * + * @param Closure $p The predicate used for filtering. + * @return Collection A collection with the results of the filter operation. + */ + public function filter(Closure $p) + { + return new static(array_filter($this->_elements, $p)); + } + + /** + * Applies the given predicate p to all elements of this collection, + * returning true, if the predicate yields true for all elements. + * + * @param Closure $p The predicate. + * @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. + */ + public function forAll(Closure $p) + { + foreach ($this->_elements as $key => $element) { + if ( ! $p($key, $element)) { + return false; + } + } + + return true; + } + + /** + * Partitions this collection in two collections according to a predicate. + * Keys are preserved in the resulting collections. + * + * @param Closure $p The predicate on which to partition. + * @return array An array with two elements. The first element contains the collection + * of elements where the predicate returned TRUE, the second element + * contains the collection of elements where the predicate returned FALSE. + */ + public function partition(Closure $p) + { + $coll1 = $coll2 = array(); + foreach ($this->_elements as $key => $element) { + if ($p($key, $element)) { + $coll1[$key] = $element; + } else { + $coll2[$key] = $element; + } + } + return array(new static($coll1), new static($coll2)); + } + + /** + * Returns a string representation of this object. + * + * @return string + */ + public function __toString() + { + return __CLASS__ . '@' . spl_object_hash($this); + } + + /** + * Clears the collection. + */ + public function clear() + { + $this->_elements = array(); + } + + /** + * Extract a slice of $length elements starting at position $offset from the Collection. + * + * If $length is null it returns all elements from $offset to the end of the Collection. + * Keys have to be preserved by this method. Calling this method will only return the + * selected slice and NOT change the elements contained in the collection slice is called on. + * + * @param int $offset + * @param int $length + * @return array + */ + public function slice($offset, $length = null) + { + return array_slice($this->_elements, $offset, $length, true); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Collections/Collection.php b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Collection.php new file mode 100644 index 0000000..9fca659 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Collections/Collection.php @@ -0,0 +1,243 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Closure, Countable, IteratorAggregate, ArrayAccess; + +/** + * The missing (SPL) Collection/Array/OrderedMap interface. + * + * A Collection resembles the nature of a regular PHP array. That is, + * it is essentially an ordered map that can also be used + * like a list. + * + * A Collection has an internal iterator just like a PHP array. In addition, + * a Collection can be iterated with external iterators, which is preferrable. + * To use an external iterator simply use the foreach language construct to + * iterate over the collection (which calls {@link getIterator()} internally) or + * explicitly retrieve an iterator though {@link getIterator()} which can then be + * used to iterate over the collection. + * You can not rely on the internal iterator of the collection being at a certain + * position unless you explicitly positioned it before. Prefer iteration with + * external iterators. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface Collection extends Countable, IteratorAggregate, ArrayAccess +{ + /** + * Adds an element at the end of the collection. + * + * @param mixed $element The element to add. + * @return boolean Always TRUE. + */ + function add($element); + + /** + * Clears the collection, removing all elements. + */ + function clear(); + + /** + * Checks whether an element is contained in the collection. + * This is an O(n) operation, where n is the size of the collection. + * + * @param mixed $element The element to search for. + * @return boolean TRUE if the collection contains the element, FALSE otherwise. + */ + function contains($element); + + /** + * Checks whether the collection is empty (contains no elements). + * + * @return boolean TRUE if the collection is empty, FALSE otherwise. + */ + function isEmpty(); + + /** + * Removes the element at the specified index from the collection. + * + * @param string|integer $key The kex/index of the element to remove. + * @return mixed The removed element or NULL, if the collection did not contain the element. + */ + function remove($key); + + /** + * Removes the specified element from the collection, if it is found. + * + * @param mixed $element The element to remove. + * @return boolean TRUE if this collection contained the specified element, FALSE otherwise. + */ + function removeElement($element); + + /** + * Checks whether the collection contains an element with the specified key/index. + * + * @param string|integer $key The key/index to check for. + * @return boolean TRUE if the collection contains an element with the specified key/index, + * FALSE otherwise. + */ + function containsKey($key); + + /** + * Gets the element at the specified key/index. + * + * @param string|integer $key The key/index of the element to retrieve. + * @return mixed + */ + function get($key); + + /** + * Gets all keys/indices of the collection. + * + * @return array The keys/indices of the collection, in the order of the corresponding + * elements in the collection. + */ + function getKeys(); + + /** + * Gets all values of the collection. + * + * @return array The values of all elements in the collection, in the order they + * appear in the collection. + */ + function getValues(); + + /** + * Sets an element in the collection at the specified key/index. + * + * @param string|integer $key The key/index of the element to set. + * @param mixed $value The element to set. + */ + function set($key, $value); + + /** + * Gets a native PHP array representation of the collection. + * + * @return array + */ + function toArray(); + + /** + * Sets the internal iterator to the first element in the collection and + * returns this element. + * + * @return mixed + */ + function first(); + + /** + * Sets the internal iterator to the last element in the collection and + * returns this element. + * + * @return mixed + */ + function last(); + + /** + * Gets the key/index of the element at the current iterator position. + * + */ + function key(); + + /** + * Gets the element of the collection at the current iterator position. + * + */ + function current(); + + /** + * Moves the internal iterator position to the next element. + * + */ + function next(); + + /** + * Tests for the existence of an element that satisfies the given predicate. + * + * @param Closure $p The predicate. + * @return boolean TRUE if the predicate is TRUE for at least one element, FALSE otherwise. + */ + function exists(Closure $p); + + /** + * Returns all the elements of this collection that satisfy the predicate p. + * The order of the elements is preserved. + * + * @param Closure $p The predicate used for filtering. + * @return Collection A collection with the results of the filter operation. + */ + function filter(Closure $p); + + /** + * Applies the given predicate p to all elements of this collection, + * returning true, if the predicate yields true for all elements. + * + * @param Closure $p The predicate. + * @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. + */ + function forAll(Closure $p); + + /** + * Applies the given function to each element in the collection and returns + * a new collection with the elements returned by the function. + * + * @param Closure $func + * @return Collection + */ + function map(Closure $func); + + /** + * Partitions this collection in two collections according to a predicate. + * Keys are preserved in the resulting collections. + * + * @param Closure $p The predicate on which to partition. + * @return array An array with two elements. The first element contains the collection + * of elements where the predicate returned TRUE, the second element + * contains the collection of elements where the predicate returned FALSE. + */ + function partition(Closure $p); + + /** + * Gets the index/key of a given element. The comparison of two elements is strict, + * that means not only the value but also the type must match. + * For objects this means reference equality. + * + * @param mixed $element The element to search for. + * @return mixed The key/index of the element or FALSE if the element was not found. + */ + function indexOf($element); + + /** + * Extract a slice of $length elements starting at position $offset from the Collection. + * + * If $length is null it returns all elements from $offset to the end of the Collection. + * Keys have to be preserved by this method. Calling this method will only return the + * selected slice and NOT change the elements contained in the collection slice is called on. + * + * @param int $offset + * @param int $length + * @return array + */ + function slice($offset, $length = null); +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php b/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php new file mode 100644 index 0000000..8c5669b --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Base exception class for package Doctrine\Common + * @author heinrich + * + */ +class CommonException extends \Exception { +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php b/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php new file mode 100644 index 0000000..bc95d30 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php @@ -0,0 +1,47 @@ +. + */ + + +namespace Doctrine\Common; + +/** + * Comparable interface that allows to compare two value objects to each other for similarity. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + */ +interface Comparable +{ + /** + * Compare the current object to the passed $other. + * + * Returns 0 if they are semantically equal, 1 if the other object + * is less than the current one, or -1 if its more than the current one. + * + * This method should not check for identity using ===, only for semantical equality for example + * when two different DateTime instances point to the exact same Date + TZ. + * + * @return int + */ + public function compareTo($other); +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/EventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/EventArgs.php new file mode 100644 index 0000000..6a3c069 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/EventArgs.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\Common; + +/** + * EventArgs is the base class for classes containing event data. + * + * This class contains no event data. It is used by events that do not pass state + * information to an event handler when an event is raised. The single empty EventArgs + * instance can be obtained through {@link getEmptyInstance}. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EventArgs +{ + /** + * @var EventArgs Single instance of EventArgs + * @static + */ + private static $_emptyEventArgsInstance; + + /** + * Gets the single, empty and immutable EventArgs instance. + * + * This instance will be used when events are dispatched without any parameter, + * like this: EventManager::dispatchEvent('eventname'); + * + * The benefit from this is that only one empty instance is instantiated and shared + * (otherwise there would be instances for every dispatched in the abovementioned form) + * + * @see EventManager::dispatchEvent + * @link http://msdn.microsoft.com/en-us/library/system.eventargs.aspx + * @static + * @return EventArgs + */ + public static function getEmptyInstance() + { + if ( ! self::$_emptyEventArgsInstance) { + self::$_emptyEventArgsInstance = new EventArgs; + } + + return self::$_emptyEventArgsInstance; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/EventManager.php b/vendor/doctrine/common/lib/Doctrine/Common/EventManager.php new file mode 100644 index 0000000..a1f11ed --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/EventManager.php @@ -0,0 +1,136 @@ +. + */ + +namespace Doctrine\Common; + +/** + * The EventManager is the central point of Doctrine's event listener system. + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EventManager +{ + /** + * Map of registered listeners. + * => + * + * @var array + */ + private $_listeners = array(); + + /** + * Dispatches an event to all registered listeners. + * + * @param string $eventName The name of the event to dispatch. The name of the event is + * the name of the method that is invoked on listeners. + * @param EventArgs $eventArgs The event arguments to pass to the event handlers/listeners. + * If not supplied, the single empty EventArgs instance is used. + * @return boolean + */ + public function dispatchEvent($eventName, EventArgs $eventArgs = null) + { + if (isset($this->_listeners[$eventName])) { + $eventArgs = $eventArgs === null ? EventArgs::getEmptyInstance() : $eventArgs; + + foreach ($this->_listeners[$eventName] as $listener) { + $listener->$eventName($eventArgs); + } + } + } + + /** + * Gets the listeners of a specific event or all listeners. + * + * @param string $event The name of the event. + * @return array The event listeners for the specified event, or all event listeners. + */ + public function getListeners($event = null) + { + return $event ? $this->_listeners[$event] : $this->_listeners; + } + + /** + * Checks whether an event has any registered listeners. + * + * @param string $event + * @return boolean TRUE if the specified event has any listeners, FALSE otherwise. + */ + public function hasListeners($event) + { + return isset($this->_listeners[$event]) && $this->_listeners[$event]; + } + + /** + * Adds an event listener that listens on the specified events. + * + * @param string|array $events The event(s) to listen on. + * @param object $listener The listener object. + */ + public function addEventListener($events, $listener) + { + // Picks the hash code related to that listener + $hash = spl_object_hash($listener); + + foreach ((array) $events as $event) { + // Overrides listener if a previous one was associated already + // Prevents duplicate listeners on same event (same instance only) + $this->_listeners[$event][$hash] = $listener; + } + } + + /** + * Removes an event listener from the specified events. + * + * @param string|array $events + * @param object $listener + */ + public function removeEventListener($events, $listener) + { + // Picks the hash code related to that listener + $hash = spl_object_hash($listener); + + foreach ((array) $events as $event) { + // Check if actually have this listener associated + if (isset($this->_listeners[$event][$hash])) { + unset($this->_listeners[$event][$hash]); + } + } + } + + /** + * Adds an EventSubscriber. The subscriber is asked for all the events he is + * interested in and added as a listener for these events. + * + * @param Doctrine\Common\EventSubscriber $subscriber The subscriber. + */ + public function addEventSubscriber(EventSubscriber $subscriber) + { + $this->addEventListener($subscriber->getSubscribedEvents(), $subscriber); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/EventSubscriber.php b/vendor/doctrine/common/lib/Doctrine/Common/EventSubscriber.php new file mode 100644 index 0000000..ed3383f --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/EventSubscriber.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\Common; + +/** + * An EventSubscriber knows himself what events he is interested in. + * If an EventSubscriber is added to an EventManager, the manager invokes + * {@link getSubscribedEvents} and registers the subscriber as a listener for all + * returned events. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface EventSubscriber +{ + /** + * Returns an array of events this subscriber wants to listen to. + * + * @return array + */ + function getSubscribedEvents(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php b/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php new file mode 100644 index 0000000..8df3b84 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php @@ -0,0 +1,266 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Base class for writing simple lexers, i.e. for creating small DSLs. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @todo Rename: AbstractLexer + */ +abstract class Lexer +{ + /** + * @var array Array of scanned tokens + */ + private $tokens = array(); + + /** + * @var integer Current lexer position in input string + */ + private $position = 0; + + /** + * @var integer Current peek of current lexer position + */ + private $peek = 0; + + /** + * @var array The next token in the input. + */ + public $lookahead; + + /** + * @var array The last matched/seen token. + */ + public $token; + + /** + * Sets the input data to be tokenized. + * + * The Lexer is immediately reset and the new input tokenized. + * Any unprocessed tokens from any previous input are lost. + * + * @param string $input The input to be tokenized. + */ + public function setInput($input) + { + $this->tokens = array(); + $this->reset(); + $this->scan($input); + } + + /** + * Resets the lexer. + */ + public function reset() + { + $this->lookahead = null; + $this->token = null; + $this->peek = 0; + $this->position = 0; + } + + /** + * Resets the peek pointer to 0. + */ + public function resetPeek() + { + $this->peek = 0; + } + + /** + * Resets the lexer position on the input to the given position. + * + * @param integer $position Position to place the lexical scanner + */ + public function resetPosition($position = 0) + { + $this->position = $position; + } + + /** + * Checks whether a given token matches the current lookahead. + * + * @param integer|string $token + * @return boolean + */ + public function isNextToken($token) + { + return null !== $this->lookahead && $this->lookahead['type'] === $token; + } + + /** + * Checks whether any of the given tokens matches the current lookahead + * + * @param array $tokens + * @return boolean + */ + public function isNextTokenAny(array $tokens) + { + return null !== $this->lookahead && in_array($this->lookahead['type'], $tokens, true); + } + + /** + * Moves to the next token in the input string. + * + * A token is an associative array containing three items: + * - 'value' : the string value of the token in the input string + * - 'type' : the type of the token (identifier, numeric, string, input + * parameter, none) + * - 'position' : the position of the token in the input string + * + * @return array|null the next token; null if there is no more tokens left + */ + public function moveNext() + { + $this->peek = 0; + $this->token = $this->lookahead; + $this->lookahead = (isset($this->tokens[$this->position])) + ? $this->tokens[$this->position++] : null; + + return $this->lookahead !== null; + } + + /** + * Tells the lexer to skip input tokens until it sees a token with the given value. + * + * @param $type The token type to skip until. + */ + public function skipUntil($type) + { + while ($this->lookahead !== null && $this->lookahead['type'] !== $type) { + $this->moveNext(); + } + } + + /** + * Checks if given value is identical to the given token + * + * @param mixed $value + * @param integer $token + * @return boolean + */ + public function isA($value, $token) + { + return $this->getType($value) === $token; + } + + /** + * Moves the lookahead token forward. + * + * @return array | null The next token or NULL if there are no more tokens ahead. + */ + public function peek() + { + if (isset($this->tokens[$this->position + $this->peek])) { + return $this->tokens[$this->position + $this->peek++]; + } else { + return null; + } + } + + /** + * Peeks at the next token, returns it and immediately resets the peek. + * + * @return array|null The next token or NULL if there are no more tokens ahead. + */ + public function glimpse() + { + $peek = $this->peek(); + $this->peek = 0; + return $peek; + } + + /** + * Scans the input string for tokens. + * + * @param string $input a query string + */ + protected function scan($input) + { + static $regex; + + if ( ! isset($regex)) { + $regex = '/(' . implode(')|(', $this->getCatchablePatterns()) . ')|' + . implode('|', $this->getNonCatchablePatterns()) . '/i'; + } + + $flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE; + $matches = preg_split($regex, $input, -1, $flags); + + foreach ($matches as $match) { + // Must remain before 'value' assignment since it can change content + $type = $this->getType($match[0]); + + $this->tokens[] = array( + 'value' => $match[0], + 'type' => $type, + 'position' => $match[1], + ); + } + } + + /** + * Gets the literal for a given token. + * + * @param integer $token + * @return string + */ + public function getLiteral($token) + { + $className = get_class($this); + $reflClass = new \ReflectionClass($className); + $constants = $reflClass->getConstants(); + + foreach ($constants as $name => $value) { + if ($value === $token) { + return $className . '::' . $name; + } + } + + return $token; + } + + /** + * Lexical catchable patterns. + * + * @return array + */ + abstract protected function getCatchablePatterns(); + + /** + * Lexical non-catchable patterns. + * + * @return array + */ + abstract protected function getNonCatchablePatterns(); + + /** + * Retrieve token type. Also processes the token value if necessary. + * + * @param string $value + * @return integer + */ + abstract protected function getType(&$value); +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php b/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php new file mode 100644 index 0000000..93e504a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Contract for classes that provide the service of notifying listeners of + * changes to their properties. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface NotifyPropertyChanged +{ + /** + * Adds a listener that wants to be notified about property changes. + * + * @param PropertyChangedListener $listener + */ + function addPropertyChangedListener(PropertyChangedListener $listener); +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php new file mode 100644 index 0000000..f045286 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php @@ -0,0 +1,218 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +use Doctrine\Common\Persistence\ManagerRegistry; + +/** + * Abstract implementation of the ManagerRegistry contract. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabien Potencier + * @author Benjamin Eberlei + * @author Lukas Kahwe Smith + */ +abstract class AbstractManagerRegistry implements ManagerRegistry +{ + private $name; + private $connections; + private $managers; + private $defaultConnection; + private $defaultManager; + private $proxyInterfaceName; + + public function __construct($name, array $connections, array $managers, $defaultConnection, $defaultManager, $proxyInterfaceName) + { + $this->name = $name; + $this->connections = $connections; + $this->managers = $managers; + $this->defaultConnection = $defaultConnection; + $this->defaultManager = $defaultManager; + $this->proxyInterfaceName = $proxyInterfaceName; + } + + /** + * Fetches/creates the given services + * + * A service in this context is connection or a manager instance + * + * @param string $name name of the service + * @return object instance of the given service + */ + abstract protected function getService($name); + + /** + * Resets the given services + * + * A service in this context is connection or a manager instance + * + * @param string $name name of the service + * @return void + */ + abstract protected function resetService($name); + + /** + * Get the name of the registry + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @inheritdoc + */ + public function getConnection($name = null) + { + if (null === $name) { + $name = $this->defaultConnection; + } + + if (!isset($this->connections[$name])) { + throw new \InvalidArgumentException(sprintf('Doctrine %s Connection named "%s" does not exist.', $this->name, $name)); + } + + return $this->getService($this->connections[$name]); + } + + /** + * @inheritdoc + */ + public function getConnectionNames() + { + return $this->connections; + } + + /** + * @inheritdoc + */ + public function getConnections() + { + $connections = array(); + foreach ($this->connections as $name => $id) { + $connections[$name] = $this->getService($id); + } + + return $connections; + } + + /** + * @inheritdoc + */ + public function getDefaultConnectionName() + { + return $this->defaultConnection; + } + + /** + * @inheritdoc + */ + public function getDefaultManagerName() + { + return $this->defaultManager; + } + + /** + * @inheritdoc + */ + public function getManager($name = null) + { + if (null === $name) { + $name = $this->defaultManager; + } + + if (!isset($this->managers[$name])) { + throw new \InvalidArgumentException(sprintf('Doctrine %s Manager named "%s" does not exist.', $this->name, $name)); + } + + return $this->getService($this->managers[$name]); + } + + /** + * @inheritdoc + */ + public function getManagerForClass($class) + { + $proxyClass = new \ReflectionClass($class); + if ($proxyClass->implementsInterface($this->proxyInterfaceName)) { + $class = $proxyClass->getParentClass()->getName(); + } + + foreach ($this->managers as $id) { + $manager = $this->getService($id); + + if (!$manager->getMetadataFactory()->isTransient($class)) { + return $manager; + } + } + } + + /** + * @inheritdoc + */ + public function getManagerNames() + { + return $this->managers; + } + + /** + * @inheritdoc + */ + public function getManagers() + { + $dms = array(); + foreach ($this->managers as $name => $id) { + $dms[$name] = $this->getService($id); + } + + return $dms; + } + + /** + * @inheritdoc + */ + public function getRepository($persistentObjectName, $persistentManagerName = null) + { + return $this->getManager($persistentManagerName)->getRepository($persistentObjectName); + } + + /** + * @inheritdoc + */ + public function resetManager($name = null) + { + if (null === $name) { + $name = $this->defaultManager; + } + + if (!isset($this->managers[$name])) { + throw new \InvalidArgumentException(sprintf('Doctrine %s Manager named "%s" does not exist.', $this->name, $name)); + } + + // force the creation of a new document manager + // if the current one is closed + $this->resetService($this->managers[$name]); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ConnectionRegistry.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ConnectionRegistry.php new file mode 100644 index 0000000..a47727f --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ConnectionRegistry.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract covering connection for a Doctrine persistence layer ManagerRegistry class to implement. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabien Potencier + * @author Benjamin Eberlei + * @author Lukas Kahwe Smith + */ +interface ConnectionRegistry +{ + /** + * Gets the default connection name. + * + * @return string The default connection name + */ + function getDefaultConnectionName(); + + /** + * Gets the named connection. + * + * @param string $name The connection name (null for the default one) + * + * @return Connection + */ + function getConnection($name = null); + + /** + * Gets an array of all registered connections + * + * @return array An array of Connection instances + */ + function getConnections(); + + /** + * Gets all connection names. + * + * @return array An array of connection names + */ + function getConnectionNames(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php new file mode 100644 index 0000000..8055b66 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php @@ -0,0 +1,77 @@ +. +*/ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions + * of entities. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LifecycleEventArgs extends EventArgs +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var object + */ + private $entity; + + /** + * Constructor + * + * @param object $entity + * @param ObjectManager $objectManager + */ + public function __construct($entity, ObjectManager $objectManager) + { + $this->entity = $entity; + $this->objectManager = $objectManager; + } + + /** + * Retireve associated Entity. + * + * @return object + */ + public function getEntity() + { + return $this->entity; + } + + /** + * Retrieve associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php new file mode 100644 index 0000000..4a18d16 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * Class that holds event arguments for a loadMetadata event. + * + * @author Jonathan H. Wage + * @since 2.2 + */ +class LoadClassMetadataEventArgs extends EventArgs +{ + /** + * @var ClassMetadata + */ + private $classMetadata; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * Constructor. + * + * @param ClasseMetadata $classMetadata + * @param ObjectManager $objectManager + */ + public function __construct(ClassMetadata $classMetadata, ObjectManager $objectManager) + { + $this->classMetadata = $classMetadata; + $this->objectManager = $objectManager; + } + + /** + * Retrieve associated ClassMetadata. + * + * @return ClassMetadata + */ + public function getClassMetadata() + { + return $this->classMetadata; + } + + /** + * Retrieve associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/ManagerEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/ManagerEventArgs.php new file mode 100644 index 0000000..33c4d79 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/ManagerEventArgs.php @@ -0,0 +1,59 @@ +. +*/ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Provides event arguments for the preFlush event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class ManagerEventArgs extends \Doctrine\Common\EventArgs +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * Constructor. + * + * @param ObjectManager $objectManager + */ + public function __construct(ObjectManager $objectManager) + { + $this->objectManager = $objectManager; + } + + /** + * Retrieve associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php new file mode 100644 index 0000000..f67ab50 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +/** + * Provides event arguments for the onClear event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class OnClearEventArgs extends \Doctrine\Common\EventArgs +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var string + */ + private $entityClass; + + /** + * Constructor. + * + * @param ObjectManager $objectManager + * @param string $entityClass Optional entity class + */ + public function __construct($objectManager, $entityClass = null) + { + $this->objectManager = $objectManager; + $this->entityClass = $entityClass; + } + + /** + * Retrieve associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } + + /** + * Name of the entity class that is cleared, or empty if all are cleared. + * + * @return string + */ + public function getEntityClass() + { + return $this->entityClass; + } + + /** + * Check if event clears all entities. + * + * @return bool + */ + public function clearsAllEntities() + { + return ($this->entityClass === null); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php new file mode 100644 index 0000000..191d053 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php @@ -0,0 +1,129 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs, + Doctrine\Common\Persistence\ObjectManager; + +/** + * Class that holds event arguments for a preUpdate event. + * + * @author Guilherme Blanco + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.2 + */ +class PreUpdateEventArgs extends LifecycleEventArgs +{ + /** + * @var array + */ + private $entityChangeSet; + + /** + * Constructor. + * + * @param object $entity + * @param ObjectManager $objectManager + * @param array $changeSet + */ + public function __construct($entity, ObjectManager $objectManager, array &$changeSet) + { + parent::__construct($entity, $objectManager); + + $this->entityChangeSet = &$changeSet; + } + + /** + * Retrieve entity changeset. + * + * @return array + */ + public function getEntityChangeSet() + { + return $this->entityChangeSet; + } + + /** + * Check if field has a changeset. + * + * @return boolean + */ + public function hasChangedField($field) + { + return isset($this->entityChangeSet[$field]); + } + + /** + * Get the old value of the changeset of the changed field. + * + * @param string $field + * @return mixed + */ + public function getOldValue($field) + { + $this->assertValidField($field); + + return $this->entityChangeSet[$field][0]; + } + + /** + * Get the new value of the changeset of the changed field. + * + * @param string $field + * @return mixed + */ + public function getNewValue($field) + { + $this->assertValidField($field); + + return $this->entityChangeSet[$field][1]; + } + + /** + * Set the new value of this field. + * + * @param string $field + * @param mixed $value + */ + public function setNewValue($field, $value) + { + $this->assertValidField($field); + + $this->entityChangeSet[$field][1] = $value; + } + + /** + * Assert the field exists in changeset. + * + * @param string $field + */ + private function assertValidField($field) + { + if ( ! isset($this->entityChangeSet[$field])) { + throw new \InvalidArgumentException(sprintf( + 'Field "%s" is not a valid field of the entity "%s" in PreUpdateEventArgs.', + $field, + get_class($this->getEntity()) + )); + } + } +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ManagerRegistry.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ManagerRegistry.php new file mode 100644 index 0000000..4d92426 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ManagerRegistry.php @@ -0,0 +1,112 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract covering object managers for a Doctrine persistence layer ManagerRegistry class to implement. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabien Potencier + * @author Benjamin Eberlei + * @author Lukas Kahwe Smith + */ +interface ManagerRegistry extends ConnectionRegistry +{ + /** + * Gets the default object manager name. + * + * @return string The default object manager name + */ + function getDefaultManagerName(); + + /** + * Gets a named object manager. + * + * @param string $name The object manager name (null for the default one) + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + function getManager($name = null); + + /** + * Gets an array of all registered object managers + * + * @return array An array of ObjectManager instances + */ + function getManagers(); + + /** + * Resets a named object manager. + * + * This method is useful when an object manager has been closed + * because of a rollbacked transaction AND when you think that + * it makes sense to get a new one to replace the closed one. + * + * Be warned that you will get a brand new object manager as + * the existing one is not useable anymore. This means that any + * other object with a dependency on this object manager will + * hold an obsolete reference. You can inject the registry instead + * to avoid this problem. + * + * @param string $name The object manager name (null for the default one) + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + function resetManager($name = null); + + /** + * Resolves a registered namespace alias to the full namespace. + * + * This method looks for the alias in all registered object managers. + * + * @param string $alias The alias + * + * @return string The full namespace + */ + function getAliasNamespace($alias); + + /** + * Gets all connection names. + * + * @return array An array of connection names + */ + function getManagerNames(); + + /** + * Gets the ObjectRepository for an persistent object. + * + * @param string $persistentObject The name of the persistent object. + * @param string $persistentManagerName The object manager name (null for the default one) + * + * @return \Doctrine\Common\Persistence\ObjectRepository + */ + function getRepository($persistentObject, $persistentManagerName = null); + + /** + * Gets the object manager associated with a given class. + * + * @param string $class A persistent object class name + * + * @return \Doctrine\Common\Persistence\ObjectManager|null + */ + function getManagerForClass($class); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php new file mode 100644 index 0000000..a2a6185 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php @@ -0,0 +1,359 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +use Doctrine\Common\Cache\Cache; + +/** + * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the + * metadata mapping informations of a class which describes how a class should be mapped + * to a relational database. + * + * This class was abstracted from the ORM ClassMetadataFactory + * + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class AbstractClassMetadataFactory implements ClassMetadataFactory +{ + /** + * Salt used by specific Object Manager implementation. + * + * @var string + */ + protected $cacheSalt = "\$CLASSMETADATA"; + + /** + * @var \Doctrine\Common\Cache\Cache + */ + private $cacheDriver; + + /** + * @var array + */ + private $loadedMetadata = array(); + + /** + * @var bool + */ + protected $initialized = false; + + /** + * @var ReflectionService + */ + private $reflectionService; + + /** + * Sets the cache driver used by the factory to cache ClassMetadata instances. + * + * @param Doctrine\Common\Cache\Cache $cacheDriver + */ + public function setCacheDriver(Cache $cacheDriver = null) + { + $this->cacheDriver = $cacheDriver; + } + + /** + * Gets the cache driver used by the factory to cache ClassMetadata instances. + * + * @return Doctrine\Common\Cache\Cache + */ + public function getCacheDriver() + { + return $this->cacheDriver; + } + + /** + * Return an array of all the loaded metadata currently in memory. + * + * @return array + */ + public function getLoadedMetadata() + { + return $this->loadedMetadata; + } + + /** + * Forces the factory to load the metadata of all classes known to the underlying + * mapping driver. + * + * @return array The ClassMetadata instances of all mapped classes. + */ + public function getAllMetadata() + { + if ( ! $this->initialized) { + $this->initialize(); + } + + $driver = $this->getDriver(); + $metadata = array(); + foreach ($driver->getAllClassNames() as $className) { + $metadata[] = $this->getMetadataFor($className); + } + + return $metadata; + } + + /** + * Lazy initialization of this stuff, especially the metadata driver, + * since these are not needed at all when a metadata cache is active. + * + * @return void + */ + abstract protected function initialize(); + + /** + * Get the fully qualified class-name from the namespace alias. + * + * @param string $namespaceAlias + * @param string $simpleClassName + * @return string + */ + abstract protected function getFqcnFromAlias($namespaceAlias, $simpleClassName); + + /** + * Return the mapping driver implementation. + * + * @return MappingDriver + */ + abstract protected function getDriver(); + + /** + * Wakeup reflection after ClassMetadata gets unserialized from cache. + * + * @param ClassMetadata $class + * @param ReflectionService $reflService + * @return void + */ + abstract protected function wakeupReflection(ClassMetadata $class, ReflectionService $reflService); + + /** + * Initialize Reflection after ClassMetadata was constructed. + * + * @param ClassMetadata $class + * @param ReflectionSErvice $reflService + * @return void + */ + abstract protected function initializeReflection(ClassMetadata $class, ReflectionService $reflService); + + /** + * Gets the class metadata descriptor for a class. + * + * @param string $className The name of the class. + * @return Doctrine\Common\Persistence\Mapping\ClassMetadata + */ + public function getMetadataFor($className) + { + if ( ! isset($this->loadedMetadata[$className])) { + $realClassName = $className; + + // Check for namespace alias + if (strpos($className, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $className); + $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + + if (isset($this->loadedMetadata[$realClassName])) { + // We do not have the alias name in the map, include it + $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + + return $this->loadedMetadata[$realClassName]; + } + } + + if ($this->cacheDriver) { + if (($cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt)) !== false) { + $this->loadedMetadata[$realClassName] = $cached; + $this->wakeupReflection($cached, $this->getReflectionService()); + } else { + foreach ($this->loadMetadata($realClassName) as $loadedClassName) { + $this->cacheDriver->save( + $loadedClassName . $this->cacheSalt, $this->loadedMetadata[$loadedClassName], null + ); + } + } + } else { + $this->loadMetadata($realClassName); + } + + if ($className != $realClassName) { + // We do not have the alias name in the map, include it + $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + } + } + + return $this->loadedMetadata[$className]; + } + + /** + * Checks whether the factory has the metadata for a class loaded already. + * + * @param string $className + * @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise. + */ + public function hasMetadataFor($className) + { + return isset($this->loadedMetadata[$className]); + } + + /** + * Sets the metadata descriptor for a specific class. + * + * NOTE: This is only useful in very special cases, like when generating proxy classes. + * + * @param string $className + * @param ClassMetadata $class + */ + public function setMetadataFor($className, $class) + { + $this->loadedMetadata[$className] = $class; + } + + /** + * Get array of parent classes for the given entity class + * + * @param string $name + * @return array $parentClasses + */ + protected function getParentClasses($name) + { + // Collect parent classes, ignoring transient (not-mapped) classes. + $parentClasses = array(); + foreach (array_reverse($this->getReflectionService()->getParentClasses($name)) as $parentClass) { + if ( ! $this->getDriver()->isTransient($parentClass)) { + $parentClasses[] = $parentClass; + } + } + return $parentClasses; + } + + /** + * Loads the metadata of the class in question and all it's ancestors whose metadata + * is still not loaded. + * + * @param string $name The name of the class for which the metadata should get loaded. + * @param array $tables The metadata collection to which the loaded metadata is added. + */ + protected function loadMetadata($name) + { + if ( ! $this->initialized) { + $this->initialize(); + } + + $loaded = array(); + + $parentClasses = $this->getParentClasses($name); + $parentClasses[] = $name; + + // Move down the hierarchy of parent classes, starting from the topmost class + $parent = null; + $rootEntityFound = false; + $visited = array(); + $reflService = $this->getReflectionService(); + foreach ($parentClasses as $className) { + if (isset($this->loadedMetadata[$className])) { + $parent = $this->loadedMetadata[$className]; + if (isset($parent->isMappedSuperclass) && $parent->isMappedSuperclass === false) { + $rootEntityFound = true; + array_unshift($visited, $className); + } + continue; + } + + $class = $this->newClassMetadataInstance($className); + $this->initializeReflection($class, $reflService); + + $this->doLoadMetadata($class, $parent, $rootEntityFound); + + $this->loadedMetadata[$className] = $class; + + $parent = $class; + + if (isset($parent->isMappedSuperclass) && $class->isMappedSuperclass === false) { + $rootEntityFound = true; + array_unshift($visited, $className); + } + + $this->wakeupReflection($class, $reflService); + + $loaded[] = $className; + } + + return $loaded; + } + + /** + * Actually load the metadata from the underlying metadata + * + * @param ClassMetadata $class + * @param ClassMetadata $parent + * @param bool $rootEntityFound + * @return void + */ + abstract protected function doLoadMetadata($class, $parent, $rootEntityFound); + + /** + * Creates a new ClassMetadata instance for the given class name. + * + * @param string $className + * @return ClassMetadata + */ + abstract protected function newClassMetadataInstance($className); + + /** + * Check if this class is mapped by this Object Manager + ClassMetadata configuration + * + * @param $class + * @return bool + */ + public function isTransient($class) + { + if ( ! $this->initialized) { + $this->initialize(); + } + + return $this->getDriver()->isTransient($class); + } + + /** + * Set reflectionService. + * + * @param ReflectionService $reflectionService + */ + public function setReflectionService(ReflectionService $reflectionService) + { + $this->reflectionService = $reflectionService; + } + + /** + * Get the reflection service associated with this metadata factory. + * + * @return ReflectionService + */ + public function getReflectionService() + { + if ($this->reflectionService === null) { + $this->reflectionService = new RuntimeReflectionService(); + } + return $this->reflectionService; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php new file mode 100644 index 0000000..705d59a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php @@ -0,0 +1,165 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * Contract for a Doctrine persistence layer ClassMetadata class to implement. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ClassMetadata +{ + /** + * Get fully-qualified class name of this persistent class. + * + * @return string + */ + function getName(); + + /** + * Gets the mapped identifier field name. + * + * The returned structure is an array of the identifier field names. + * + * @return array + */ + function getIdentifier(); + + /** + * Gets the ReflectionClass instance for this mapped class. + * + * @return ReflectionClass + */ + function getReflectionClass(); + + /** + * Checks if the given field name is a mapped identifier for this class. + * + * @param string $fieldName + * @return boolean + */ + function isIdentifier($fieldName); + + /** + * Checks if the given field is a mapped property for this class. + * + * @param string $fieldName + * @return boolean + */ + function hasField($fieldName); + + /** + * Checks if the given field is a mapped association for this class. + * + * @param string $fieldName + * @return boolean + */ + function hasAssociation($fieldName); + + /** + * Checks if the given field is a mapped single valued association for this class. + * + * @param string $fieldName + * @return boolean + */ + function isSingleValuedAssociation($fieldName); + + /** + * Checks if the given field is a mapped collection valued association for this class. + * + * @param string $fieldName + * @return boolean + */ + function isCollectionValuedAssociation($fieldName); + + /** + * A numerically indexed list of field names of this persistent class. + * + * This array includes identifier fields if present on this class. + * + * @return array + */ + function getFieldNames(); + + /** + * Returns an array of identifier field names numerically indexed. + * + * @return array + */ + function getIdentifierFieldNames(); + + /** + * A numerically indexed list of association names of this persistent class. + * + * This array includes identifier associations if present on this class. + * + * @return array + */ + function getAssociationNames(); + + /** + * Returns a type name of this field. + * + * This type names can be implementation specific but should at least include the php types: + * integer, string, boolean, float/double, datetime. + * + * @param string $fieldName + * @return string + */ + function getTypeOfField($fieldName); + + /** + * Returns the target class name of the given association. + * + * @param string $assocName + * @return string + */ + function getAssociationTargetClass($assocName); + + /** + * Checks if the association is the inverse side of a bidirectional association + * + * @param string $assocName + * @return boolean + */ + function isAssociationInverseSide($assocName); + + /** + * Returns the target field of the owning side of the association + * + * @param string $assocName + * @return string + */ + function getAssociationMappedByTargetField($assocName); + + /** + * Return the identifier of this object as an array with field name as key. + * + * Has to return an empty array if no identifier isset. + * + * @param object $object + * @return array + */ + function getIdentifierValues($object); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php new file mode 100644 index 0000000..bf27ba9 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php @@ -0,0 +1,74 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * Contract for a Doctrine persistence layer ClassMetadata class to implement. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ClassMetadataFactory +{ + /** + * Forces the factory to load the metadata of all classes known to the underlying + * mapping driver. + * + * @return array The ClassMetadata instances of all mapped classes. + */ + function getAllMetadata(); + + /** + * Gets the class metadata descriptor for a class. + * + * @param string $className The name of the class. + * @return ClassMetadata + */ + function getMetadataFor($className); + + /** + * Checks whether the factory has the metadata for a class loaded already. + * + * @param string $className + * @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise. + */ + function hasMetadataFor($className); + + /** + * Sets the metadata descriptor for a specific class. + * + * @param string $className + * @param ClassMetadata $class + */ + function setMetadataFor($className, $class); + + /** + * Whether the class with the specified name should have its metadata loaded. + * This is only the case if it is either mapped directly or as a + * MappedSuperclass. + * + * @param string $className + * @return boolean + */ + function isTransient($className); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php new file mode 100644 index 0000000..f52d37e --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php @@ -0,0 +1,214 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Cache\ArrayCache, + Doctrine\Common\Annotations\AnnotationReader, + Doctrine\Common\Annotations\AnnotationRegistry, + Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * The AnnotationDriver reads the mapping metadata from docblock annotations. + * + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +abstract class AnnotationDriver implements MappingDriver +{ + /** + * The AnnotationReader. + * + * @var AnnotationReader + */ + protected $reader; + + /** + * The paths where to look for mapping files. + * + * @var array + */ + protected $paths = array(); + + /** + * The file extension of mapping documents. + * + * @var string + */ + protected $fileExtension = '.php'; + + /** + * Cache for AnnotationDriver#getAllClassNames() + * + * @var array + */ + protected $classNames; + + /** + * Name of the entity annotations as keys + * + * @var array + */ + protected $entityAnnotationClasses = array(); + + /** + * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading + * docblock annotations. + * + * @param AnnotationReader $reader The AnnotationReader to use, duck-typed. + * @param string|array $paths One or multiple paths where mapping classes can be found. + */ + public function __construct($reader, $paths = null) + { + $this->reader = $reader; + if ($paths) { + $this->addPaths((array) $paths); + } + } + + /** + * Append lookup paths to metadata driver. + * + * @param array $paths + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * Retrieve the defined metadata lookup paths. + * + * @return array + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Retrieve the current annotation reader + * + * @return AnnotationReader + */ + public function getReader() + { + return $this->reader; + } + + /** + * Get the file extension used to look for mapping files under + * + * @return void + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Set the file extension used to look for mapping files under + * + * @param string $fileExtension The file extension to set + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * Whether the class with the specified name is transient. Only non-transient + * classes, that is entities and mapped superclasses, should have their metadata loaded. + * + * A class is non-transient if it is annotated with an annotation + * from the {@see AnnotationDriver::entityAnnotationClasses}. + * + * @param string $className + * @return boolean + */ + public function isTransient($className) + { + $classAnnotations = $this->reader->getClassAnnotations(new \ReflectionClass($className)); + + foreach ($classAnnotations as $annot) { + if (isset($this->entityAnnotationClasses[get_class($annot)])) { + return false; + } + } + return true; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + if ($this->classNames !== null) { + return $this->classNames; + } + + if (!$this->paths) { + throw MappingException::pathRequired(); + } + + $classes = array(); + $includedFiles = array(); + + foreach ($this->paths as $path) { + if ( ! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RegexIterator( + new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::LEAVES_ONLY + ), + '/^.+' . str_replace('.', '\.', $this->fileExtension) . '$/i', + \RecursiveRegexIterator::GET_MATCH + ); + + foreach ($iterator as $file) { + $sourceFile = realpath($file[0]); + + require_once $sourceFile; + + $includedFiles[] = $sourceFile; + } + } + + $declared = get_declared_classes(); + + foreach ($declared as $className) { + $rc = new \ReflectionClass($className); + $sourceFile = $rc->getFileName(); + if (in_array($sourceFile, $includedFiles) && ! $this->isTransient($className)) { + $classes[] = $className; + } + } + + $this->classNames = $classes; + + return $classes; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php new file mode 100644 index 0000000..efaf545 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php @@ -0,0 +1,169 @@ +. +*/ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * Locate the file that contains the metadata information for a given class name. + * + * This behavior is inpependent of the actual content of the file. It just detects + * the file which is responsible for the given class name. + * + * @author Benjamin Eberlei + * @author Johannes M. Schmitt + */ +class DefaultFileLocator implements FileLocator +{ + /** + * The paths where to look for mapping files. + * + * @var array + */ + protected $paths = array(); + + /** + * The file extension of mapping documents. + * + * @var string + */ + protected $fileExtension; + + /** + * Initializes a new FileDriver that looks in the given path(s) for mapping + * documents and operates in the specified operating mode. + * + * @param string|array $paths One or multiple paths where mapping documents can be found. + */ + public function __construct($paths, $fileExtension = null) + { + $this->addPaths((array) $paths); + $this->fileExtension = $fileExtension; + } + + /** + * Append lookup paths to metadata driver. + * + * @param array $paths + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * Retrieve the defined metadata lookup paths. + * + * @return array + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Get the file extension used to look for mapping files under + * + * @return void + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Set the file extension used to look for mapping files under + * + * @param string $fileExtension The file extension to set + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * {@inheritDoc} + */ + public function findMappingFile($className) + { + $fileName = str_replace('\\', '.', $className) . $this->fileExtension; + + // Check whether file exists + foreach ($this->paths as $path) { + if (file_exists($path . DIRECTORY_SEPARATOR . $fileName)) { + return $path . DIRECTORY_SEPARATOR . $fileName; + } + } + + throw MappingException::mappingFileNotFound($className, $fileName); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames($globalBasename) + { + $classes = array(); + + if ($this->paths) { + foreach ($this->paths as $path) { + if ( ! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + $fileName = $file->getBasename($this->fileExtension); + + if ($fileName == $file->getBasename() || $fileName == $globalBasename) { + continue; + } + + // NOTE: All files found here means classes are not transient! + $classes[] = str_replace('.', '\\', $fileName); + } + } + } + + return $classes; + } + + /** + * {@inheritDoc} + */ + public function fileExists($className) + { + $fileName = str_replace('\\', '.', $className) . $this->fileExtension; + + // Check whether file exists + foreach ((array) $this->paths as $path) { + if (file_exists($path . DIRECTORY_SEPARATOR . $fileName)) { + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php new file mode 100644 index 0000000..22cf117 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php @@ -0,0 +1,178 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * Base driver for file-based metadata drivers. + * + * A file driver operates in a mode where it loads the mapping files of individual + * classes on demand. This requires the user to adhere to the convention of 1 mapping + * file per class and the file names of the mapping files must correspond to the full + * class name, including namespace, with the namespace delimiters '\', replaced by dots '.'. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +abstract class FileDriver implements MappingDriver +{ + /** + * @var FileLocator + */ + protected $locator; + + /** + * @var array + */ + protected $classCache; + + /** + * @var string + */ + protected $globalBasename; + + /** + * Initializes a new FileDriver that looks in the given path(s) for mapping + * documents and operates in the specified operating mode. + * + * @param string|array|FileLocator $paths A FileLocator or one/multiple paths where mapping documents can be found. + * @param string $fileExtension + */ + public function __construct($locator, $fileExtension = null) + { + if ($locator instanceof FileLocator) { + $this->locator = $locator; + } else { + $this->locator = new DefaultFileLocator((array)$locator, $fileExtension); + } + } + + public function setGlobalBasename($file) + { + $this->globalBasename = $file; + } + + public function getGlobalBasename() + { + return $this->globalBasename; + } + + /** + * Get the element of schema meta data for the class from the mapping file. + * This will lazily load the mapping file if it is not loaded yet + * + * @return array $element The element of schema meta data + */ + public function getElement($className) + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (isset($this->classCache[$className])) { + return $this->classCache[$className]; + } + + $result = $this->loadMappingFile($this->locator->findMappingFile($className)); + + return $result[$className]; + } + + /** + * Whether the class with the specified name should have its metadata loaded. + * This is only the case if it is either mapped as an Entity or a + * MappedSuperclass. + * + * @param string $className + * @return boolean + */ + public function isTransient($className) + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (isset($this->classCache[$className])) { + return false; + } + + return !$this->locator->fileExists($className); + } + + /** + * Gets the names of all mapped classes known to this driver. + * + * @return array The names of all mapped classes known to this driver. + */ + public function getAllClassNames() + { + if ($this->classCache === null) { + $this->initialize(); + } + + $classNames = (array)$this->locator->getAllClassNames($this->globalBasename); + if ($this->classCache) { + $classNames = array_merge(array_keys($this->classCache), $classNames); + } + return $classNames; + } + + /** + * Loads a mapping file with the given name and returns a map + * from class/entity names to their corresponding file driver elements. + * + * @param string $file The mapping file to load. + * @return array + */ + abstract protected function loadMappingFile($file); + + /** + * Initialize the class cache from all the global files. + * + * Using this feature adds a substantial performance hit to file drivers as + * more metadata has to be loaded into memory than might actually be + * necessary. This may not be relevant to scenarios where caching of + * metadata is in place, however hits very hard in scenarios where no + * caching is used. + * + * @return void + */ + protected function initialize() + { + $this->classCache = array(); + if (null !== $this->globalBasename) { + foreach ($this->locator->getPaths() as $path) { + $file = $path.'/'.$this->globalBasename.$this->locator->getFileExtension(); + if (is_file($file)) { + $this->classCache = array_merge( + $this->classCache, + $this->loadMappingFile($file) + ); + } + } + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php new file mode 100644 index 0000000..a1019d7 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php @@ -0,0 +1,69 @@ +. +*/ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +/** + * Locate the file that contains the metadata information for a given class name. + * + * This behavior is inpependent of the actual content of the file. It just detects + * the file which is responsible for the given class name. + * + * @author Benjamin Eberlei + * @author Johannes M. Schmitt + */ +interface FileLocator +{ + /** + * Locate mapping file for the given class name. + * + * @param string $className + * @return string + */ + function findMappingFile($className); + + /** + * Get all class names that are found with this file locator. + * + * @param string $globalBasename Passed to allow excluding the basename + * @return array + */ + function getAllClassNames($globalBasename); + + /** + * Check if a file can be found for this class name. + * + * @return bool + */ + function fileExists($className); + + /** + * Get all the paths that this file locator looks for mapping files. + * + * @return array + */ + function getPaths(); + + /** + * Get the file extension that mapping files are suffixed with. + * + * @return string + */ + function getFileExtension(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php new file mode 100644 index 0000000..c050d32 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * Contract for metadata drivers. + * + * @since 2.2 + * @author Jonathan H. Wage + */ +interface MappingDriver +{ + /** + * Loads the metadata for the specified class into the provided container. + * + * @param string $className + * @param ClassMetadata $metadata + */ + function loadMetadataForClass($className, ClassMetadata $metadata); + + /** + * Gets the names of all mapped classes known to this driver. + * + * @return array The names of all mapped classes known to this driver. + */ + function getAllClassNames(); + + /** + * Whether the class with the specified name should have its metadata loaded. + * This is only the case if it is either mapped as an Entity or a + * MappedSuperclass. + * + * @param string $className + * @return boolean + */ + function isTransient($className); +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php new file mode 100644 index 0000000..c7c1452 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php @@ -0,0 +1,125 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver, + Doctrine\Common\Persistence\Mapping\ClassMetadata, + Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * The DriverChain allows you to add multiple other mapping drivers for + * certain namespaces + * + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class MappingDriverChain implements MappingDriver +{ + /** + * @var array + */ + private $drivers = array(); + + /** + * Add a nested driver. + * + * @param Driver $nestedDriver + * @param string $namespace + */ + public function addDriver(MappingDriver $nestedDriver, $namespace) + { + $this->drivers[$namespace] = $nestedDriver; + } + + /** + * Get the array of nested drivers. + * + * @return array $drivers + */ + public function getDrivers() + { + return $this->drivers; + } + + /** + * Loads the metadata for the specified class into the provided container. + * + * @param string $className + * @param ClassMetadataInfo $metadata + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + foreach ($this->drivers as $namespace => $driver) { + if (strpos($className, $namespace) === 0) { + $driver->loadMetadataForClass($className, $metadata); + return; + } + } + + throw MappingException::classNotFoundInNamespaces($className, array_keys($this->drivers)); + } + + /** + * Gets the names of all mapped classes known to this driver. + * + * @return array The names of all mapped classes known to this driver. + */ + public function getAllClassNames() + { + $classNames = array(); + $driverClasses = array(); + foreach ($this->drivers AS $namespace => $driver) { + $oid = spl_object_hash($driver); + if (!isset($driverClasses[$oid])) { + $driverClasses[$oid] = $driver->getAllClassNames(); + } + + foreach ($driverClasses[$oid] AS $className) { + if (strpos($className, $namespace) === 0) { + $classNames[$className] = true; + } + } + } + return array_keys($classNames); + } + + /** + * Whether the class with the specified name should have its metadata loaded. + * + * This is only the case for non-transient classes either mapped as an Entity or MappedSuperclass. + * + * @param string $className + * @return boolean + */ + public function isTransient($className) + { + foreach ($this->drivers AS $namespace => $driver) { + if (strpos($className, $namespace) === 0) { + return $driver->isTransient($className); + } + } + + // class isTransient, i.e. not an entity or mapped superclass + return true; + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php new file mode 100644 index 0000000..7751dae --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * The PHPDriver includes php files which just populate ClassMetadataInfo + * instances with plain php code + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class PHPDriver extends FileDriver +{ + /** + * {@inheritdoc} + */ + protected $metadata; + + /** + * {@inheritDoc} + */ + public function __construct($locator, $fileExtension = null) + { + $fileExtension = ".php"; + parent::__construct($locator, $fileExtension); + } + + /** + * {@inheritdoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + $this->metadata = $metadata; + $this->loadMappingFile($this->locator->findMappingFile($className)); + } + + /** + * {@inheritdoc} + */ + protected function loadMappingFile($file) + { + $metadata = $this->metadata; + include $file; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php new file mode 100644 index 0000000..9103ed8 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php @@ -0,0 +1,131 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * The StaticPHPDriver calls a static loadMetadata() method on your entity + * classes where you can manually populate the ClassMetadata instance. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class StaticPHPDriver implements MappingDriver +{ + /** + * Paths of entity directories. + * + * @var array + */ + private $paths = array(); + + /** + * Map of all class names. + * + * @var array + */ + private $classNames; + + public function __construct($paths) + { + $this->addPaths((array) $paths); + } + + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * {@inheritdoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + $className::loadMetadata($metadata); + } + + /** + * {@inheritDoc} + * @todo Same code exists in AnnotationDriver, should we re-use it somehow or not worry about it? + */ + public function getAllClassNames() + { + if ($this->classNames !== null) { + return $this->classNames; + } + + if (!$this->paths) { + throw MappingException::pathRequired(); + } + + $classes = array(); + $includedFiles = array(); + + foreach ($this->paths as $path) { + if (!is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + if ($file->getBasename('.php') == $file->getBasename()) { + continue; + } + + $sourceFile = realpath($file->getPathName()); + require_once $sourceFile; + $includedFiles[] = $sourceFile; + } + } + + $declared = get_declared_classes(); + + foreach ($declared as $className) { + $rc = new \ReflectionClass($className); + $sourceFile = $rc->getFileName(); + if (in_array($sourceFile, $includedFiles) && !$this->isTransient($className)) { + $classes[] = $className; + } + } + + $this->classNames = $classes; + + return $classes; + } + + /** + * {@inheritdoc} + */ + public function isTransient($className) + { + return ! method_exists($className, 'loadMetadata'); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php new file mode 100644 index 0000000..d338cf6 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php @@ -0,0 +1,198 @@ +. +*/ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * The Symfony File Locator makes a simplifying assumptions compared + * to the DefaultFileLocator. By assuming paths only contain entities of a certain + * namespace the mapping files consists of the short classname only. + * + * @author Fabien Potencier + * @author Benjamin Eberlei + * @license MIT + */ +class SymfonyFileLocator implements FileLocator +{ + /** + * The paths where to look for mapping files. + * + * @var array + */ + protected $paths = array(); + + /** + * A map of mapping directory path to namespace prefix used to expand class shortnames. + * + * @var array + */ + protected $prefixes = array(); + + /** + * File extension that is searched for. + * + * @var string + */ + protected $fileExtension; + + public function __construct(array $prefixes, $fileExtension = null) + { + $this->addNamespacePrefixes($prefixes); + $this->fileExtension = $fileExtension; + } + + public function addNamespacePrefixes(array $prefixes) + { + $this->prefixes = array_merge($this->prefixes, $prefixes); + $this->paths = array_merge($this->paths, array_keys($prefixes)); + } + + public function getNamespacePrefixes() + { + return $this->prefixes; + } + + /** + * {@inheritDoc} + */ + public function getPaths() + { + return $this->paths; + } + + /** + * {@inheritDoc} + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Set the file extension used to look for mapping files under + * + * @param string $fileExtension The file extension to set + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * {@inheritDoc} + */ + public function fileExists($className) + { + $defaultFileName = str_replace('\\', '.', $className).$this->fileExtension; + foreach ($this->paths as $path) { + if (!isset($this->prefixes[$path])) { + // global namespace class + if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) { + return true; + } + + continue; + } + + $prefix = $this->prefixes[$path]; + + if (0 !== strpos($className, $prefix.'\\')) { + continue; + } + + $filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', '.').$this->fileExtension; + return is_file($filename); + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames($globalBasename = null) + { + $classes = array(); + + if ($this->paths) { + foreach ((array) $this->paths as $path) { + if (!is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + $fileName = $file->getBasename($this->fileExtension); + + if ($fileName == $file->getBasename() || $fileName == $globalBasename) { + continue; + } + + // NOTE: All files found here means classes are not transient! + if (isset($this->prefixes[$path])) { + $classes[] = $this->prefixes[$path].'\\'.str_replace('.', '\\', $fileName); + } else { + $classes[] = str_replace('.', '\\', $fileName); + } + } + } + } + + return $classes; + } + + /** + * {@inheritDoc} + */ + public function findMappingFile($className) + { + $defaultFileName = str_replace('\\', '.', $className).$this->fileExtension; + foreach ($this->paths as $path) { + if (!isset($this->prefixes[$path])) { + if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) { + return $path.DIRECTORY_SEPARATOR.$defaultFileName; + } + + continue; + } + + $prefix = $this->prefixes[$path]; + + if (0 !== strpos($className, $prefix.'\\')) { + continue; + } + + $filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', '.').$this->fileExtension; + if (is_file($filename)) { + return $filename; + } + + throw MappingException::mappingFileNotFound($className, $filename); + } + + throw MappingException::mappingFileNotFound($className, substr($className, strrpos($className, '\\') + 1).$this->fileExtension); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php new file mode 100644 index 0000000..4ecd2ad --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * A MappingException indicates that something is wrong with the mapping setup. + * + * @since 2.2 + */ +class MappingException extends \Exception +{ + public static function classNotFoundInNamespaces($className, $namespaces) + { + return new self("The class '" . $className . "' was not found in the ". + "chain configured namespaces " . implode(", ", $namespaces)); + } + + public static function pathRequired() + { + return new self("Specifying the paths to your entities is required ". + "in the AnnotationDriver to retrieve all class names."); + } + + public static function fileMappingDriversRequireConfiguredDirectoryPath($path = null) + { + if ( ! empty($path)) { + $path = '[' . $path . ']'; + } + + return new self( + 'File mapping drivers must have a valid directory path, ' . + 'however the given path ' . $path . ' seems to be incorrect!' + ); + } + + public static function mappingFileNotFound($entityName, $fileName) + { + return new self("No mapping file found named '$fileName' for class '$entityName'."); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ReflectionService.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ReflectionService.php new file mode 100644 index 0000000..4e0e312 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ReflectionService.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * Very simple reflection service abstraction. + * + * This is required inside metadata layers that may require either + * static or runtime reflection. + * + * @author Benjamin Eberlei + */ +interface ReflectionService +{ + /** + * Return an array of the parent classes (not interfaces) for the given class. + * + * @param string $class + * @return array + */ + function getParentClasses($class); + + /** + * Return the shortname of a class. + * + * @param string $class + * @return string + */ + function getClassShortName($class); + + /** + * @param string $class + * @return string + */ + function getClassNamespace($class); + + /** + * Return a reflection class instance or null + * + * @param string $class + * @return ReflectionClass|null + */ + function getClass($class); + + /** + * Return an accessible property (setAccessible(true)) or null. + * + * @param string $class + * @param string $property + * @return ReflectionProperty|null + */ + function getAccessibleProperty($class, $property); + + /** + * Check if the class have a public method with the given name. + * + * @param mixed $class + * @param mixed $method + * @return bool + */ + function hasPublicMethod($class, $method); +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php new file mode 100644 index 0000000..abcff58 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php @@ -0,0 +1,102 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +use ReflectionClass; +use ReflectionProperty; + +/** + * PHP Runtime Reflection Service + * + * @author Benjamin Eberlei + */ +class RuntimeReflectionService implements ReflectionService +{ + /** + * Return an array of the parent classes (not interfaces) for the given class. + * + * @param string $class + * @return array + */ + public function getParentClasses($class) + { + return class_parents($class); + } + + /** + * Return the shortname of a class. + * + * @param string $class + * @return string + */ + public function getClassShortName($class) + { + $r = new ReflectionClass($class); + return $r->getShortName(); + } + + /** + * @param string $class + * @return string + */ + public function getClassNamespace($class) + { + $r = new ReflectionClass($class); + return $r->getNamespaceName(); + } + + /** + * Return a reflection class instance or null + * + * @param string $class + * @return ReflectionClass|null + */ + public function getClass($class) + { + return new ReflectionClass($class); + } + + /** + * Return an accessible property (setAccessible(true)) or null. + * + * @param string $class + * @param string $property + * @return ReflectionProperty|null + */ + public function getAccessibleProperty($class, $property) + { + $property = new ReflectionProperty($class, $property); + $property->setAccessible(true); + return $property; + } + + /** + * Check if the class have a public method with the given name. + * + * @param mixed $class + * @param mixed $method + * @return bool + */ + public function hasPublicMethod($class, $method) + { + return method_exists($class, $method) && is_callable(array($class, $method)); + } +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php new file mode 100644 index 0000000..2de6e76 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +use ReflectionClass; +use ReflectionProperty; + +/** + * PHP Runtime Reflection Service + * + * @author Benjamin Eberlei + */ +class StaticReflectionService implements ReflectionService +{ + /** + * Return an array of the parent classes (not interfaces) for the given class. + * + * @param string $class + * @return array + */ + public function getParentClasses($class) + { + return array(); + } + + /** + * Return the shortname of a class. + * + * @param string $className + * @return string + */ + public function getClassShortName($className) + { + if (strpos($className, '\\') !== false) { + $className = substr($className, strrpos($className, "\\")+1); + } + return $className; + } + + /** + * Return the namespace of a class. + * + * @param string $className + * @return string + */ + public function getClassNamespace($className) + { + $namespace = ''; + if (strpos($className, '\\') !== false) { + $namespace = strrev(substr( strrev($className), strpos(strrev($className), '\\')+1 )); + } + return $namespace; + } + + /** + * Return a reflection class instance or null + * + * @param string $class + * @return ReflectionClass|null + */ + public function getClass($class) + { + return null; + } + + /** + * Return an accessible property (setAccessible(true)) or null. + * + * @param string $class + * @param string $property + * @return ReflectionProperty|null + */ + public function getAccessibleProperty($class, $property) + { + return null; + } + + /** + * Check if the class have a public method with the given name. + * + * @param mixed $class + * @param mixed $method + * @return bool + */ + public function hasPublicMethod($class, $method) + { + return method_exists($class, $method) && is_callable(array($class, $method)); + } +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManager.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManager.php new file mode 100644 index 0000000..6d70fc1 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManager.php @@ -0,0 +1,143 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract for a Doctrine persistence layer ObjectManager class to implement. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ObjectManager +{ + /** + * Finds a object by its identifier. + * + * This is just a convenient shortcut for getRepository($className)->find($id). + * + * @param string + * @param mixed + * @return object + */ + function find($className, $id); + + /** + * Tells the ObjectManager to make an instance managed and persistent. + * + * The object will be entered into the database as a result of the flush operation. + * + * NOTE: The persist operation always considers objects that are not yet known to + * this ObjectManager as NEW. Do not pass detached objects to the persist operation. + * + * @param object $object The instance to make managed and persistent. + */ + function persist($object); + + /** + * Removes an object instance. + * + * A removed object will be removed from the database as a result of the flush operation. + * + * @param object $object The object instance to remove. + */ + function remove($object); + + /** + * Merges the state of a detached object into the persistence context + * of this ObjectManager and returns the managed copy of the object. + * The object passed to merge will not become associated/managed with this ObjectManager. + * + * @param object $object + */ + function merge($object); + + /** + * Detaches an object from the ObjectManager, causing a managed object to + * become detached. Unflushed changes made to the object if any + * (including removal of the object), will not be synchronized to the database. + * Objects which previously referenced the detached object will continue to + * reference it. + * + * @param object $object The object to detach. + */ + function detach($object); + + /** + * Refreshes the persistent state of an object from the database, + * overriding any local changes that have not yet been persisted. + * + * @param object $object The object to refresh. + */ + function refresh($object); + + /** + * Flushes all changes to objects that have been queued up to now to the database. + * This effectively synchronizes the in-memory state of managed objects with the + * database. + */ + function flush(); + + /** + * Gets the repository for a class. + * + * @param string $className + * @return \Doctrine\Common\Persistence\ObjectRepository + */ + function getRepository($className); + + /** + * Returns the ClassMetadata descriptor for a class. + * + * The class name must be the fully-qualified class name without a leading backslash + * (as it is returned by get_class($obj)). + * + * @param string $className + * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata + */ + function getClassMetadata($className); + + /** + * Gets the metadata factory used to gather the metadata of classes. + * + * @return Doctrine\Common\Persistence\Mapping\ClassMetadataFactory + */ + function getMetadataFactory(); + + /** + * Helper method to initialize a lazy loading proxy or persistent collection. + * + * This method is a no-op for other objects. + * + * @param object $obj + */ + function initializeObject($obj); + + /** + * Check if the object is part of the current UnitOfWork and therefore + * managed. + * + * @param object $object + * @return bool + */ + function contains($object); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerAware.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerAware.php new file mode 100644 index 0000000..015dd3d --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerAware.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * Makes a Persistent Objects aware of its own object-manager. + * + * Using this interface the managing object manager and class metadata instances + * are injected into the persistent object after construction. This allows + * you to implement ActiveRecord functionality on top of the persistance-ignorance + * that Doctrine propagates. + * + * Word of Warning: This is a very powerful hook to change how you can work with your domain models. + * Using this hook will break the Single Responsibility Principle inside your Domain Objects + * and increase the coupling of database and objects. + * + * Every ObjectManager has to implement this functionality itself. + * + * @author Benjamin Eberlei + */ +interface ObjectManagerAware +{ + /** + * Injects responsible ObjectManager and the ClassMetadata into this persistent object. + * + * @param ObjectManager $objectManager + * @param ClassMetadata $classMetadata + */ + public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectRepository.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectRepository.php new file mode 100644 index 0000000..2263328 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectRepository.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract for a Doctrine persistence layer ObjectRepository class to implement. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ObjectRepository +{ + /** + * Finds an object by its primary key / identifier. + * + * @param $id The identifier. + * @return object The object. + */ + function find($id); + + /** + * Finds all objects in the repository. + * + * @return mixed The objects. + */ + function findAll(); + + /** + * Finds objects by a set of criteria. + * + * Optionally sorting and limiting details can be passed. An implementation may throw + * an UnexpectedValueException if certain values of the sorting or limiting details are + * not supported. + * + * @throws UnexpectedValueException + * @param array $criteria + * @param array|null $orderBy + * @param int|null $limit + * @param int|null $offset + * @return mixed The objects. + */ + function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null); + + /** + * Finds a single object by a set of criteria. + * + * @param array $criteria + * @return object The object. + */ + function findOneBy(array $criteria); + + /** + * Returns the class name of the object managed by the repository + * + * @return string + */ + function getClassName(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/PersistentObject.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/PersistentObject.php new file mode 100644 index 0000000..4274af6 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/PersistentObject.php @@ -0,0 +1,233 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; + +/** + * PersistentObject base class that implements getter/setter methods for all mapped fields and associations + * by overriding __call. + * + * This class is a forward compatible implementation of the PersistentObject trait. + * + * + * Limitations: + * + * 1. All persistent objects have to be associated with a single ObjectManager, multiple + * ObjectManagers are not supported. You can set the ObjectManager with `PersistentObject#setObjectManager()`. + * 2. Setters and getters only work if a ClassMetadata instance was injected into the PersistentObject. + * This is either done on `postLoad` of an object or by accessing the global object manager. + * 3. There are no hooks for setters/getters. Just implement the method yourself instead of relying on __call(). + * 4. Slower than handcoded implementations: An average of 7 method calls per access to a field and 11 for an association. + * 5. Only the inverse side associations get autoset on the owning side aswell. Setting objects on the owning side + * will not set the inverse side associations. + * + * @example + * + * PersistentObject::setObjectManager($em); + * + * class Foo extends PersistentObject + * { + * private $id; + * } + * + * $foo = new Foo(); + * $foo->getId(); // method exists through __call + * + * @author Benjamin Eberlei + */ +abstract class PersistentObject implements ObjectManagerAware +{ + /** + * @var ObjectManager + */ + private static $objectManager; + + /** + * @var ClassMetadata + */ + private $cm; + + /** + * Set the object manager responsible for all persistent object base classes. + * + * @param ObjectManager $objectManager + */ + static public function setObjectManager(ObjectManager $objectManager = null) + { + self::$objectManager = $objectManager; + } + + /** + * @return ObjectManager + */ + static public function getObjectManager() + { + return self::$objectManager; + } + + /** + * Inject Doctrine Object Manager + * + * @param ObjectManager $objectManager + * @param ClassMetadata $classMetadata + */ + public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata) + { + if ($objectManager !== self::$objectManager) { + throw new \RuntimeException("Trying to use PersistentObject with different ObjectManager instances. " . + "Was PersistentObject::setObjectManager() called?"); + } + + $this->cm = $classMetadata; + } + + /** + * Sets a persistent fields value. + * + * @throws InvalidArgumentException - When the wrong target object type is passed to an association + * @throws BadMethodCallException - When no persistent field exists by that name. + * @param string $field + * @param array $args + * @return void + */ + private function set($field, $args) + { + $this->initializeDoctrine(); + + if ($this->cm->hasField($field) && !$this->cm->isIdentifier($field)) { + $this->$field = $args[0]; + } else if ($this->cm->hasAssociation($field) && $this->cm->isSingleValuedAssociation($field)) { + $targetClass = $this->cm->getAssociationTargetClass($field); + if (!($args[0] instanceof $targetClass) && $args[0] !== null) { + throw new \InvalidArgumentException("Expected persistent object of type '".$targetClass."'"); + } + $this->$field = $args[0]; + $this->completeOwningSide($field, $targetClass, $args[0]); + } else { + throw new \BadMethodCallException("no field with name '".$field."' exists on '".$this->cm->getName()."'"); + } + } + + /** + * Get persistent field value. + * + * @throws BadMethodCallException - When no persistent field exists by that name. + * @param string $field + * @return mixed + */ + private function get($field) + { + $this->initializeDoctrine(); + + if ( $this->cm->hasField($field) || $this->cm->hasAssociation($field) ) { + return $this->$field; + } else { + throw new \BadMethodCallException("no field with name '".$field."' exists on '".$this->cm->getName()."'"); + } + } + + /** + * If this is an inverse side association complete the owning side. + * + * @param string $field + * @param ClassMetadata $targetClass + * @param object $targetObject + */ + private function completeOwningSide($field, $targetClass, $targetObject) + { + // add this object on the owning side aswell, for obvious infinite recursion + // reasons this is only done when called on the inverse side. + if ($this->cm->isAssociationInverseSide($field)) { + $mappedByField = $this->cm->getAssociationMappedByTargetField($field); + $targetMetadata = self::$objectManager->getClassMetadata($targetClass); + + $setter = ($targetMetadata->isCollectionValuedAssociation($mappedByField) ? "add" : "set").$mappedByField; + $targetObject->$setter($this); + } + } + + /** + * Add an object to a collection + * + * @param type $field + * @param assoc $args + */ + private function add($field, $args) + { + $this->initializeDoctrine(); + + if ($this->cm->hasAssociation($field) && $this->cm->isCollectionValuedAssociation($field)) { + $targetClass = $this->cm->getAssociationTargetClass($field); + if (!($args[0] instanceof $targetClass)) { + throw new \InvalidArgumentException("Expected persistent object of type '".$targetClass."'"); + } + if (!($this->$field instanceof Collection)) { + $this->$field = new ArrayCollection($this->$field ?: array()); + } + $this->$field->add($args[0]); + $this->completeOwningSide($field, $targetClass, $args[0]); + } else { + throw new \BadMethodCallException("There is no method add".$field."() on ".$this->cm->getName()); + } + } + + /** + * Initialize Doctrine Metadata for this class. + * + * @return void + */ + private function initializeDoctrine() + { + if ($this->cm !== null) { + return; + } + + if (!self::$objectManager) { + throw new \RuntimeException("No runtime object manager set. Call PersistentObject#setObjectManager()."); + } + + $this->cm = self::$objectManager->getClassMetadata(get_class($this)); + } + + /** + * Magic method that implements + * + * @param string $method + * @param array $args + * @return mixed + */ + public function __call($method, $args) + { + $command = substr($method, 0, 3); + $field = lcfirst(substr($method, 3)); + if ($command == "set") { + $this->set($field, $args); + } else if ($command == "get") { + return $this->get($field); + } else if ($command == "add") { + $this->add($field, $args); + } else { + throw new \BadMethodCallException("There is no method ".$method." on ".$this->cm->getName()); + } + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Proxy.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Proxy.php new file mode 100644 index 0000000..726979f --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Proxy.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Interface for proxy classes. + * + * @author Roman Borschel + * @since 2.2 + */ +interface Proxy +{ + /** + * Marker for Proxy class names. + * + * @var string + */ + const MARKER = '__CG__'; + + /** + * Length of the proxy marker + * + * @var int + */ + const MARKER_LENGTH = 6; + + /** + * Initialize this proxy if its not yet initialized. + * + * Acts as a no-op if already initialized. + * + * @return void + */ + public function __load(); + + /** + * Is this proxy initialized or not. + * + * @return bool + */ + public function __isInitialized(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php b/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php new file mode 100644 index 0000000..87c5b41 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Contract for classes that are potential listeners of a NotifyPropertyChanged + * implementor. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface PropertyChangedListener +{ + /** + * Notifies the listener of a property change. + * + * @param object $sender The object on which the property changed. + * @param string $propertyName The name of the property that changed. + * @param mixed $oldValue The old value of the property that changed. + * @param mixed $newValue The new value of the property that changed. + */ + function propertyChanged($sender, $propertyName, $oldValue, $newValue); +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php b/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php new file mode 100644 index 0000000..c346278 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\Common\Util; + +use Doctrine\Common\Persistence\Proxy; + +/** + * Class and reflection related functionality for objects that + * might or not be proxy objects at the moment. + * + * @author Benjamin Eberlei + * @author Johannes Schmitt + */ +class ClassUtils +{ + /** + * Get the real class name of a class name that could be a proxy. + * + * @param string + * @return string + */ + public static function getRealClass($class) + { + if (false === $pos = strrpos($class, '\\'.Proxy::MARKER.'\\')) { + return $class; + } + + return substr($class, $pos + Proxy::MARKER_LENGTH + 2); + } + + /** + * Get the real class name of an object (even if its a proxy) + * + * @param object + * @return string + */ + public static function getClass($object) + { + return self::getRealClass(get_class($object)); + } + + /** + * Get the real parent class name of a class or object + * + * @param string + * @return string + */ + public static function getParentClass($className) + { + return get_parent_class( self::getRealClass( $className ) ); + } + + /** + * Create a new reflection class + * + * @param string + * @return ReflectionClass + */ + public static function newReflectionClass($class) + { + return new \ReflectionClass( self::getRealClass( $class ) ); + } + + /** + * Create a new reflection object + * + * @param object + * @return ReflectionObject + */ + public static function newReflectionObject($object) + { + return self::newReflectionClass( self::getClass( $object ) ); + } + + /** + * Given a class name and a proxy namespace return the proxy name. + * + * @param string $className + * @param string $proxyNamespace + * @return string + */ + public static function generateProxyClassName($className, $proxyNamespace) + { + return rtrim($proxyNamespace, '\\') . '\\'.Proxy::MARKER.'\\' . ltrim($className, '\\'); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php b/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php new file mode 100644 index 0000000..57ae312 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php @@ -0,0 +1,123 @@ +. + */ + +namespace Doctrine\Common\Util; + +/** + * Static class containing most used debug methods. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Giorgio Sironi + */ +final class Debug +{ + /** + * Private constructor (prevents from instantiation) + * + */ + private function __construct() {} + + /** + * Prints a dump of the public, protected and private properties of $var. + * + * @static + * @link http://xdebug.org/ + * @param mixed $var + * @param integer $maxDepth Maximum nesting level for object properties + * @param boolean $stripTags Flag that indicate if output should strip HTML tags + */ + public static function dump($var, $maxDepth = 2, $stripTags = true) + { + ini_set('html_errors', 'On'); + + if (extension_loaded('xdebug')) { + ini_set('xdebug.var_display_max_depth', $maxDepth); + } + + $var = self::export($var, $maxDepth++); + + ob_start(); + var_dump($var); + $dump = ob_get_contents(); + ob_end_clean(); + + echo ($stripTags ? strip_tags(html_entity_decode($dump)) : $dump); + + ini_set('html_errors', 'Off'); + } + + public static function export($var, $maxDepth) + { + $return = null; + $isObj = is_object($var); + + if ($isObj && in_array('Doctrine\Common\Collections\Collection', class_implements($var))) { + $var = $var->toArray(); + } + + if ($maxDepth) { + if (is_array($var)) { + $return = array(); + + foreach ($var as $k => $v) { + $return[$k] = self::export($v, $maxDepth - 1); + } + } else if ($isObj) { + $return = new \stdclass(); + if ($var instanceof \DateTime) { + $return->__CLASS__ = "DateTime"; + $return->date = $var->format('c'); + $return->timezone = $var->getTimeZone()->getName(); + } else { + $reflClass = ClassUtils::newReflectionObject($var); + $return->__CLASS__ = ClassUtils::getClass($var); + + if ($var instanceof \Doctrine\Common\Persistence\Proxy) { + $return->__IS_PROXY__ = true; + $return->__PROXY_INITIALIZED__ = $var->__isInitialized(); + } + + foreach ($reflClass->getProperties() as $reflProperty) { + $name = $reflProperty->getName(); + + $reflProperty->setAccessible(true); + $return->$name = self::export($reflProperty->getValue($var), $maxDepth - 1); + } + } + } else { + $return = $var; + } + } else { + $return = is_object($var) ? get_class($var) + : (is_array($var) ? 'Array(' . count($var) . ')' : $var); + } + + return $return; + } + + public static function toString($obj) + { + return method_exists('__toString', $obj) ? (string) $obj : get_class($obj) . '@' . spl_object_hash($obj); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Util/Inflector.php b/vendor/doctrine/common/lib/Doctrine/Common/Util/Inflector.php new file mode 100644 index 0000000..ba1eb17 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/Inflector.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\Common\Util; + +/** + * Doctrine inflector has static methods for inflecting text + * + * The methods in these classes are from several different sources collected + * across several different php projects and several different authors. The + * original author names and emails are not known + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 1.0 + * @version $Revision: 3189 $ + * @author Konsta Vesterinen + * @author Jonathan H. Wage + */ +class Inflector +{ + /** + * Convert word in to the format for a Doctrine table name. Converts 'ModelName' to 'model_name' + * + * @param string $word Word to tableize + * @return string $word Tableized word + */ + public static function tableize($word) + { + return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word)); + } + + /** + * Convert a word in to the format for a Doctrine class name. Converts 'table_name' to 'TableName' + * + * @param string $word Word to classify + * @return string $word Classified word + */ + public static function classify($word) + { + return str_replace(" ", "", ucwords(strtr($word, "_-", " "))); + } + + /** + * Camelize a word. This uses the classify() method and turns the first character to lowercase + * + * @param string $word + * @return string $word + */ + public static function camelize($word) + { + return lcfirst(self::classify($word)); + } +} \ No newline at end of file diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Version.php b/vendor/doctrine/common/lib/Doctrine/Common/Version.php new file mode 100644 index 0000000..b7c9a82 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Version.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Class to store and retrieve the version of Doctrine + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Version +{ + /** + * Current Doctrine Version + */ + const VERSION = '2.2.2'; + + /** + * Compares a Doctrine version with the current one. + * + * @param string $version Doctrine version to compare. + * @return int Returns -1 if older, 0 if it is the same, 1 if version + * passed as argument is newer. + */ + public static function compare($version) + { + $currentVersion = str_replace(' ', '', strtolower(self::VERSION)); + $version = str_replace(' ', '', $version); + + return version_compare($version, $currentVersion); + } +} diff --git a/vendor/doctrine/common/phpunit.xml.dist b/vendor/doctrine/common/phpunit.xml.dist new file mode 100644 index 0000000..b9d3b34 --- /dev/null +++ b/vendor/doctrine/common/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/ + + + + + + performance + + + diff --git a/vendor/doctrine/dbal/.travis.yml b/vendor/doctrine/dbal/.travis.yml new file mode 100644 index 0000000..ab6fde0 --- /dev/null +++ b/vendor/doctrine/dbal/.travis.yml @@ -0,0 +1,20 @@ +language: php + +php: + - 5.3 + - 5.4 +env: + - DB=mysql + - DB=pgsql + - DB=sqlite + +before_script: + - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS doctrine_tests;' -U postgres; fi" + - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS doctrine_tests_tmp;' -U postgres; fi" + - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests;' -U postgres; fi" + - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests_tmp;' -U postgres; fi" + - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'create database IF NOT EXISTS doctrine_tests_tmp;create database IF NOT EXISTS doctrine_tests;'; fi" + - git submodule update --init + +script: phpunit --configuration tests/travis/$DB.travis.xml + diff --git a/vendor/doctrine/dbal/LICENSE b/vendor/doctrine/dbal/LICENSE new file mode 100644 index 0000000..1c03f74 --- /dev/null +++ b/vendor/doctrine/dbal/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/vendor/doctrine/dbal/README.md b/vendor/doctrine/dbal/README.md new file mode 100644 index 0000000..70617c3 --- /dev/null +++ b/vendor/doctrine/dbal/README.md @@ -0,0 +1,14 @@ +# Doctrine DBAL + +Powerful database abstraction layer with many features for database schema introspection, schema management and PDO abstraction. + +* Master: [![Build Status](https://secure.travis-ci.org/doctrine/dbal.png?branch=master)](http://travis-ci.org/doctrine/dbal) +* Master: [![Build Status](https://secure.travis-ci.org/doctrine/dbal.png?branch=2.2)](http://travis-ci.org/doctrine/dbal) +* 2.1.x: [![Build Status](https://secure.travis-ci.org/doctrine/dbal.png?branch=2.1.x)](http://travis-ci.org/doctrine/dbal) + +## More resources: + +* [Website](http://www.doctrine-project.org) +* [Documentation](http://www.doctrine-project.org/projects/dbal/current/docs/en) +* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DBAL) +* [Downloads](http://github.com/doctrine/dbal/downloads) diff --git a/vendor/doctrine/dbal/UPGRADE b/vendor/doctrine/dbal/UPGRADE new file mode 100644 index 0000000..9466d7a --- /dev/null +++ b/vendor/doctrine/dbal/UPGRADE @@ -0,0 +1,72 @@ +# Upgrade to 2.2 + +## Doctrine\DBAL\Connection#insert and Doctrine\DBAL\Connnection#update + +Both methods now accept an optional last parameter $types with binding types of the values passed. +This can potentially break child classes that have overwritten one of these methods. + +## Doctrine\DBAL\Connection#executeQuery + +Doctrine\DBAL\Connection#executeQuery() got a new last parameter "QueryCacheProfile $qcp" + +## Doctrine\DBAL\Driver\Statement split + +The Driver statement was split into a ResultStatement and the normal statement extending from it. +This seperates the configuration and the retrieval API from a statement. + +## MsSql Platform/SchemaManager renamed + +The MsSqlPlatform was renamed to SQLServerPlatform, the MsSqlSchemaManager was renamed +to SQLServerSchemaManager. + +## Cleanup SQLServer Platform version mess + +DBAL 2.1 and before were actually only compatible to SQL Server 2008, not earlier versions. +Still other parts of the platform did use old features instead of newly introduced datatypes +in SQL Server 2005. Starting with DBAL 2.2 you can pick the Doctrine abstraction exactly +matching your SQL Server version. + +The PDO SqlSrv driver now uses the new `SQLServer2008Platform` as default platform. +This platform uses new features of SQL Server as of version 2008. This also includes a switch +in the used fields for "text" and "blob" field types to: + + "text" => "VARCHAR(MAX)" + "blob" => "VARBINARY(MAX)" + +Additionally `SQLServerPlatform` in DBAL 2.1 and before used "DATE", "TIME" and "DATETIME2" for dates. +This types are only available since version 2008 and the introduction of an explicit +SQLServer 2008 platform makes this dependency explicit. + +An `SQLServer2005Platform` was also introduced to differentiate the features between +versions 2003, earlier and 2005. + +With this change the `SQLServerPlatform` now throws an exception for using limit queries +with an offset, since SQLServer 2003 and lower do not support this feature. + +To use the old SQL Server Platform, because you are using SQL Server 2003 and below use +the following configuration code: + + use Doctrine\DBAL\DriverManager; + use Doctrine\DBAL\Platforms\SQLServerPlatform; + use Doctrine\DBAL\Platforms\SQLServer2005Platform; + + // You are using SQL Server 2003 or earlier + $conn = DriverManager::getConnection(array( + 'driver' => 'pdo_sqlsrv', + 'platform' => new SQLServerPlatform() + // .. additional parameters + )); + + // You are using SQL Server 2005 + $conn = DriverManager::getConnection(array( + 'driver' => 'pdo_sqlsrv', + 'platform' => new SQLServer2005Platform() + // .. additional parameters + )); + + // You are using SQL Server 2008 + $conn = DriverManager::getConnection(array( + 'driver' => 'pdo_sqlsrv', + // 2008 is default platform + // .. additional parameters + )); diff --git a/vendor/doctrine/dbal/composer.json b/vendor/doctrine/dbal/composer.json new file mode 100644 index 0000000..9fca5d3 --- /dev/null +++ b/vendor/doctrine/dbal/composer.json @@ -0,0 +1,21 @@ +{ + "name": "doctrine/dbal", + "type": "library", + "description": "Database Abstraction Layer", + "keywords": ["dbal", "database", "persistence", "queryobject"], + "homepage": "http://www.doctrine-project.org", + "license": "LGPL", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"} + ], + "require": { + "php": ">=5.3.2", + "doctrine/common": ">=2.2.0,<=2.2.99" + }, + "autoload": { + "psr-0": { "Doctrine\\DBAL": "lib/" } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php new file mode 100644 index 0000000..4818cd2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\DBAL\Driver\ResultStatement; +use PDO; + +class ArrayStatement implements \IteratorAggregate, ResultStatement +{ + private $data; + private $columnCount = 0; + private $num = 0; + private $defaultFetchStyle = PDO::FETCH_BOTH; + + public function __construct(array $data) + { + $this->data = $data; + if (count($data)) { + $this->columnCount = count($data[0]); + } + } + + public function closeCursor() + { + unset ($this->data); + } + + public function columnCount() + { + return $this->columnCount; + } + + public function setFetchMode($fetchStyle, $arg2 = null, $arg3 = null) + { + if ($arg2 !== null || $arg3 !== null) { + throw new \InvalidArgumentException("Caching layer does not support 2nd/3rd argument to setFetchMode()"); + } + + $this->defaultFetchStyle = $fetchStyle; + } + + public function getIterator() + { + $data = $this->fetchAll($this->defaultFetchStyle); + return new \ArrayIterator($data); + } + + public function fetch($fetchStyle = PDO::FETCH_BOTH) + { + if (isset($this->data[$this->num])) { + $row = $this->data[$this->num++]; + if ($fetchStyle === PDO::FETCH_ASSOC) { + return $row; + } else if ($fetchStyle === PDO::FETCH_NUM) { + return array_values($row); + } else if ($fetchStyle === PDO::FETCH_BOTH) { + return array_merge($row, array_values($row)); + } + } + return false; + } + + public function fetchAll($fetchStyle = PDO::FETCH_BOTH) + { + $rows = array(); + while ($row = $this->fetch($fetchStyle)) { + $rows[] = $row; + } + return $rows; + } + + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + if (!isset($row[$columnIndex])) { + // TODO: verify this is correct behavior + return false; + } + return $row[$columnIndex]; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/CacheException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/CacheException.php new file mode 100644 index 0000000..367ab81 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/CacheException.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +/** + * @author Benjamin Eberlei + * @since 2.2 + */ +class CacheException extends \Doctrine\DBAL\DBALException +{ + static public function noCacheKey() + { + return new self("No cache key was set."); + } + + static public function noResultDriverConfigured() + { + return new self("Trying to cache a query but no result driver is configured."); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php new file mode 100644 index 0000000..1ffbd97 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php @@ -0,0 +1,131 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\Common\Cache\Cache; + +/** + * Query Cache Profile handles the data relevant for query caching. + * + * It is a value object, setter methods return NEW instances. + * + * @author Benjamin Eberlei + */ +class QueryCacheProfile +{ + /** + * @var Cache + */ + private $resultCacheDriver; + /** + * @var int + */ + private $lifetime = 0; + /** + * @var string + */ + private $cacheKey; + + /** + * @param int $lifetime + * @param string $cacheKey + * @param Cache $resultCache + */ + public function __construct($lifetime = 0, $cacheKey = null, Cache $resultCache = null) + { + $this->lifetime = $lifetime; + $this->cacheKey = $cacheKey; + $this->resultCacheDriver = $resultCache; + } + + /** + * @return Cache + */ + public function getResultCacheDriver() + { + return $this->resultCacheDriver; + } + + /** + * @return int + */ + public function getLifetime() + { + return $this->lifetime; + } + + /** + * @return string + */ + public function getCacheKey() + { + if ($this->cacheKey === null) { + throw CacheException::noCacheKey(); + } + return $this->cacheKey; + } + + /** + * Generate the real cache key from query, params and types. + * + * @param string $query + * @param array $params + * @param array $types + * @return array + */ + public function generateCacheKeys($query, $params, $types) + { + $realCacheKey = $query . "-" . serialize($params) . "-" . serialize($types); + // should the key be automatically generated using the inputs or is the cache key set? + if ($this->cacheKey === null) { + $cacheKey = sha1($realCacheKey); + } else { + $cacheKey = $this->cacheKey; + } + return array($cacheKey, $realCacheKey); + } + + /** + * @param Cache $cache + * @return QueryCacheProfile + */ + public function setResultCacheDriver(Cache $cache) + { + return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache); + } + + /** + * @param string|null $cacheKey + * @return QueryCacheProfile + */ + public function setCacheKey($cacheKey) + { + return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCacheDriver); + } + + /** + * @param int $lifetime + * @return QueryCacheProfile + */ + public function setLifetime($lifetime) + { + return new QueryCacheProfile($lifetime, $this->cacheKey, $this->resultCacheDriver); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php new file mode 100644 index 0000000..6054184 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php @@ -0,0 +1,255 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\Driver\ResultStatement; +use Doctrine\DBAL\Connection; +use Doctrine\Common\Cache\Cache; +use PDO; + +/** + * Cache statement for SQL results. + * + * A result is saved in multiple cache keys, there is the originally specified + * cache key which is just pointing to result rows by key. The following things + * have to be ensured: + * + * 1. lifetime of the original key has to be longer than that of all the individual rows keys + * 2. if any one row key is missing the query has to be re-executed. + * + * Also you have to realize that the cache will load the whole result into memory at once to ensure 2. + * This means that the memory usage for cached results might increase by using this feature. + */ +class ResultCacheStatement implements \IteratorAggregate, ResultStatement +{ + /** + * @var \Doctrine\Common\Cache\Cache + */ + private $resultCache; + + /** + * + * @var string + */ + private $cacheKey; + + /** + * @var string + */ + private $realKey; + + /** + * @var int + */ + private $lifetime; + + /** + * @var Doctrine\DBAL\Driver\Statement + */ + private $statement; + + /** + * Did we reach the end of the statement? + * + * @var bool + */ + private $emptied = false; + + /** + * @var array + */ + private $data; + + /** + * @var int + */ + private $defaultFetchStyle = PDO::FETCH_BOTH; + + /** + * @param Statement $stmt + * @param Cache $resultCache + * @param string $cacheKey + * @param string $realKey + * @param int $lifetime + */ + public function __construct(Statement $stmt, Cache $resultCache, $cacheKey, $realKey, $lifetime) + { + $this->statement = $stmt; + $this->resultCache = $resultCache; + $this->cacheKey = $cacheKey; + $this->realKey = $realKey; + $this->lifetime = $lifetime; + } + + /** + * Closes the cursor, enabling the statement to be executed again. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + public function closeCursor() + { + $this->statement->closeCursor(); + if ($this->emptied && $this->data !== null) { + $data = $this->resultCache->fetch($this->cacheKey); + if (!$data) { + $data = array(); + } + $data[$this->realKey] = $this->data; + + $this->resultCache->save($this->cacheKey, $data, $this->lifetime); + unset($this->data); + } + } + + /** + * columnCount + * Returns the number of columns in the result set + * + * @return integer Returns the number of columns in the result set represented + * by the PDOStatement object. If there is no result set, + * this method should return 0. + */ + public function columnCount() + { + return $this->statement->columnCount(); + } + + public function setFetchMode($fetchStyle, $arg2 = null, $arg3 = null) + { + $this->defaultFetchStyle = $fetchStyle; + } + + public function getIterator() + { + $data = $this->fetchAll($this->defaultFetchStyle); + return new \ArrayIterator($data); + } + + /** + * fetch + * + * @see Query::HYDRATE_* constants + * @param integer $fetchStyle Controls how the next row will be returned to the caller. + * This value must be one of the Query::HYDRATE_* constants, + * defaulting to Query::HYDRATE_BOTH + * + * @param integer $cursorOrientation For a PDOStatement object representing a scrollable cursor, + * this value determines which row will be returned to the caller. + * This value must be one of the Query::HYDRATE_ORI_* constants, defaulting to + * Query::HYDRATE_ORI_NEXT. To request a scrollable cursor for your + * PDOStatement object, + * you must set the PDO::ATTR_CURSOR attribute to Doctrine::CURSOR_SCROLL when you + * prepare the SQL statement with Doctrine_Adapter_Interface->prepare(). + * + * @param integer $cursorOffset For a PDOStatement object representing a scrollable cursor for which the + * $cursorOrientation parameter is set to Query::HYDRATE_ORI_ABS, this value specifies + * the absolute number of the row in the result set that shall be fetched. + * + * For a PDOStatement object representing a scrollable cursor for + * which the $cursorOrientation parameter is set to Query::HYDRATE_ORI_REL, this value + * specifies the row to fetch relative to the cursor position before + * PDOStatement->fetch() was called. + * + * @return mixed + */ + public function fetch($fetchStyle = PDO::FETCH_BOTH) + { + if ($this->data === null) { + $this->data = array(); + } + + $row = $this->statement->fetch(PDO::FETCH_ASSOC); + if ($row) { + $this->data[] = $row; + + if ($fetchStyle == PDO::FETCH_ASSOC) { + return $row; + } else if ($fetchStyle == PDO::FETCH_NUM) { + return array_values($row); + } else if ($fetchStyle == PDO::FETCH_BOTH) { + return array_merge($row, array_values($row)); + } else { + throw new \InvalidArgumentException("Invalid fetch-style given for caching result."); + } + } + $this->emptied = true; + return false; + } + + /** + * Returns an array containing all of the result set rows + * + * @param integer $fetchStyle Controls how the next row will be returned to the caller. + * This value must be one of the Query::HYDRATE_* constants, + * defaulting to Query::HYDRATE_BOTH + * + * @param integer $columnIndex Returns the indicated 0-indexed column when the value of $fetchStyle is + * Query::HYDRATE_COLUMN. Defaults to 0. + * + * @return array + */ + public function fetchAll($fetchStyle = PDO::FETCH_BOTH) + { + $rows = array(); + while ($row = $this->fetch($fetchStyle)) { + $rows[] = $row; + } + return $rows; + } + + /** + * fetchColumn + * Returns a single column from the next row of a + * result set or FALSE if there are no more rows. + * + * @param integer $columnIndex 0-indexed number of the column you wish to retrieve from the row. If no + * value is supplied, PDOStatement->fetchColumn() + * fetches the first column. + * + * @return string returns a single column in the next row of a result set. + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + if (!isset($row[$columnIndex])) { + // TODO: verify this is correct behavior + return false; + } + return $row[$columnIndex]; + } + + /** + * rowCount + * rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement + * executed by the corresponding object. + * + * If the last SQL statement executed by the associated Statement object was a SELECT statement, + * some databases may return the number of rows returned by that statement. However, + * this behaviour is not guaranteed for all databases and should not be + * relied on for portable applications. + * + * @return integer Returns the number of rows. + */ + public function rowCount() + { + return $this->statement->rowCount(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Configuration.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Configuration.php new file mode 100644 index 0000000..03327fb --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Configuration.php @@ -0,0 +1,113 @@ +. + */ + +namespace Doctrine\DBAL; + +use Doctrine\DBAL\Logging\SQLLogger; +use Doctrine\Common\Cache\Cache; + +/** + * Configuration container for the Doctrine DBAL. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @internal When adding a new configuration option just write a getter/setter + * pair and add the option to the _attributes array with a proper default value. + */ +class Configuration +{ + /** + * The attributes that are contained in the configuration. + * Values are default values. + * + * @var array + */ + protected $_attributes = array(); + + /** + * Sets the SQL logger to use. Defaults to NULL which means SQL logging is disabled. + * + * @param SQLLogger $logger + */ + public function setSQLLogger(SQLLogger $logger = null) + { + $this->_attributes['sqlLogger'] = $logger; + } + + /** + * Gets the SQL logger that is used. + * + * @return SQLLogger + */ + public function getSQLLogger() + { + return isset($this->_attributes['sqlLogger']) ? + $this->_attributes['sqlLogger'] : null; + } + + /** + * Gets the cache driver implementation that is used for query result caching. + * + * @return \Doctrine\Common\Cache\Cache + */ + public function getResultCacheImpl() + { + return isset($this->_attributes['resultCacheImpl']) ? + $this->_attributes['resultCacheImpl'] : null; + } + + /** + * Sets the cache driver implementation that is used for query result caching. + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + */ + public function setResultCacheImpl(Cache $cacheImpl) + { + $this->_attributes['resultCacheImpl'] = $cacheImpl; + } + + /** + * Filter schema assets expression. + * + * Only include tables/sequences matching the filter expression regexp in + * schema instances generated for the active connection when calling + * {AbstractSchemaManager#createSchema()}. + * + * @param string $filterExpression + */ + public function setFilterSchemaAssetsExpression($filterExpression) + { + $this->_attributes['filterSchemaAssetsExpression'] = $filterExpression; + } + + /** + * Return filter schema assets expression. + * + * @return string|null + */ + public function getFilterSchemaAssetsExpression() + { + if (isset($this->_attributes['filterSchemaAssetsExpression'])) { + return $this->_attributes['filterSchemaAssetsExpression']; + } + return null; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php new file mode 100644 index 0000000..abe9376 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php @@ -0,0 +1,1168 @@ +. + */ + +namespace Doctrine\DBAL; + +use PDO, Closure, Exception, + Doctrine\DBAL\Types\Type, + Doctrine\DBAL\Driver\Connection as DriverConnection, + Doctrine\Common\EventManager, + Doctrine\DBAL\DBALException, + Doctrine\DBAL\Cache\ResultCacheStatement, + Doctrine\DBAL\Cache\QueryCacheProfile, + Doctrine\DBAL\Cache\ArrayStatement, + Doctrine\DBAL\Cache\CacheException; + +/** + * A wrapper around a Doctrine\DBAL\Driver\Connection that adds features like + * events, transaction isolation levels, configuration, emulated transaction nesting, + * lazy connecting and more. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Konsta Vesterinen + * @author Lukas Smith (MDB2 library) + * @author Benjamin Eberlei + */ +class Connection implements DriverConnection +{ + /** + * Constant for transaction isolation level READ UNCOMMITTED. + */ + const TRANSACTION_READ_UNCOMMITTED = 1; + + /** + * Constant for transaction isolation level READ COMMITTED. + */ + const TRANSACTION_READ_COMMITTED = 2; + + /** + * Constant for transaction isolation level REPEATABLE READ. + */ + const TRANSACTION_REPEATABLE_READ = 3; + + /** + * Constant for transaction isolation level SERIALIZABLE. + */ + const TRANSACTION_SERIALIZABLE = 4; + + /** + * Represents an array of ints to be expanded by Doctrine SQL parsing. + * + * @var int + */ + const PARAM_INT_ARRAY = 101; + + /** + * Represents an array of strings to be expanded by Doctrine SQL parsing. + * + * @var int + */ + const PARAM_STR_ARRAY = 102; + + /** + * Offset by which PARAM_* constants are detected as arrays of the param type. + * + * @var int + */ + const ARRAY_PARAM_OFFSET = 100; + + /** + * The wrapped driver connection. + * + * @var Doctrine\DBAL\Driver\Connection + */ + protected $_conn; + + /** + * @var Doctrine\DBAL\Configuration + */ + protected $_config; + + /** + * @var Doctrine\Common\EventManager + */ + protected $_eventManager; + + /** + * @var Doctrine\DBAL\Query\ExpressionBuilder + */ + protected $_expr; + + /** + * Whether or not a connection has been established. + * + * @var boolean + */ + private $_isConnected = false; + + /** + * The transaction nesting level. + * + * @var integer + */ + private $_transactionNestingLevel = 0; + + /** + * The currently active transaction isolation level. + * + * @var integer + */ + private $_transactionIsolationLevel; + + /** + * If nested transations should use savepoints + * + * @var integer + */ + private $_nestTransactionsWithSavepoints; + + /** + * The parameters used during creation of the Connection instance. + * + * @var array + */ + private $_params = array(); + + /** + * The DatabasePlatform object that provides information about the + * database platform used by the connection. + * + * @var Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $_platform; + + /** + * The schema manager. + * + * @var Doctrine\DBAL\Schema\SchemaManager + */ + protected $_schemaManager; + + /** + * The used DBAL driver. + * + * @var Doctrine\DBAL\Driver + */ + protected $_driver; + + /** + * Flag that indicates whether the current transaction is marked for rollback only. + * + * @var boolean + */ + private $_isRollbackOnly = false; + + /** + * Initializes a new instance of the Connection class. + * + * @param array $params The connection parameters. + * @param Driver $driver + * @param Configuration $config + * @param EventManager $eventManager + */ + public function __construct(array $params, Driver $driver, Configuration $config = null, + EventManager $eventManager = null) + { + $this->_driver = $driver; + $this->_params = $params; + + if (isset($params['pdo'])) { + $this->_conn = $params['pdo']; + $this->_isConnected = true; + } + + // Create default config and event manager if none given + if ( ! $config) { + $config = new Configuration(); + } + + if ( ! $eventManager) { + $eventManager = new EventManager(); + } + + $this->_config = $config; + $this->_eventManager = $eventManager; + + $this->_expr = new Query\Expression\ExpressionBuilder($this); + + if ( ! isset($params['platform'])) { + $this->_platform = $driver->getDatabasePlatform(); + } else if ($params['platform'] instanceof Platforms\AbstractPlatform) { + $this->_platform = $params['platform']; + } else { + throw DBALException::invalidPlatformSpecified(); + } + + $this->_platform->setEventManager($eventManager); + + $this->_transactionIsolationLevel = $this->_platform->getDefaultTransactionIsolationLevel(); + } + + /** + * Gets the parameters used during instantiation. + * + * @return array $params + */ + public function getParams() + { + return $this->_params; + } + + /** + * Gets the name of the database this Connection is connected to. + * + * @return string $database + */ + public function getDatabase() + { + return $this->_driver->getDatabase($this); + } + + /** + * Gets the hostname of the currently connected database. + * + * @return string + */ + public function getHost() + { + return isset($this->_params['host']) ? $this->_params['host'] : null; + } + + /** + * Gets the port of the currently connected database. + * + * @return mixed + */ + public function getPort() + { + return isset($this->_params['port']) ? $this->_params['port'] : null; + } + + /** + * Gets the username used by this connection. + * + * @return string + */ + public function getUsername() + { + return isset($this->_params['user']) ? $this->_params['user'] : null; + } + + /** + * Gets the password used by this connection. + * + * @return string + */ + public function getPassword() + { + return isset($this->_params['password']) ? $this->_params['password'] : null; + } + + /** + * Gets the DBAL driver instance. + * + * @return \Doctrine\DBAL\Driver + */ + public function getDriver() + { + return $this->_driver; + } + + /** + * Gets the Configuration used by the Connection. + * + * @return \Doctrine\DBAL\Configuration + */ + public function getConfiguration() + { + return $this->_config; + } + + /** + * Gets the EventManager used by the Connection. + * + * @return \Doctrine\Common\EventManager + */ + public function getEventManager() + { + return $this->_eventManager; + } + + /** + * Gets the DatabasePlatform for the connection. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_platform; + } + + /** + * Gets the ExpressionBuilder for the connection. + * + * @return \Doctrine\DBAL\Query\ExpressionBuilder + */ + public function getExpressionBuilder() + { + return $this->_expr; + } + + /** + * Establishes the connection with the database. + * + * @return boolean TRUE if the connection was successfully established, FALSE if + * the connection is already open. + */ + public function connect() + { + if ($this->_isConnected) return false; + + $driverOptions = isset($this->_params['driverOptions']) ? + $this->_params['driverOptions'] : array(); + $user = isset($this->_params['user']) ? $this->_params['user'] : null; + $password = isset($this->_params['password']) ? + $this->_params['password'] : null; + + $this->_conn = $this->_driver->connect($this->_params, $user, $password, $driverOptions); + $this->_isConnected = true; + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new Event\ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * Prepares and executes an SQL query and returns the first row of the result + * as an associative array. + * + * @param string $statement The SQL query. + * @param array $params The query parameters. + * @return array + */ + public function fetchAssoc($statement, array $params = array()) + { + return $this->executeQuery($statement, $params)->fetch(PDO::FETCH_ASSOC); + } + + /** + * Prepares and executes an SQL query and returns the first row of the result + * as a numerically indexed array. + * + * @param string $statement sql query to be executed + * @param array $params prepared statement params + * @return array + */ + public function fetchArray($statement, array $params = array()) + { + return $this->executeQuery($statement, $params)->fetch(PDO::FETCH_NUM); + } + + /** + * Prepares and executes an SQL query and returns the value of a single column + * of the first row of the result. + * + * @param string $statement sql query to be executed + * @param array $params prepared statement params + * @param int $colnum 0-indexed column number to retrieve + * @return mixed + */ + public function fetchColumn($statement, array $params = array(), $colnum = 0) + { + return $this->executeQuery($statement, $params)->fetchColumn($colnum); + } + + /** + * Whether an actual connection to the database is established. + * + * @return boolean + */ + public function isConnected() + { + return $this->_isConnected; + } + + /** + * Checks whether a transaction is currently active. + * + * @return boolean TRUE if a transaction is currently active, FALSE otherwise. + */ + public function isTransactionActive() + { + return $this->_transactionNestingLevel > 0; + } + + /** + * Executes an SQL DELETE statement on a table. + * + * @param string $table The name of the table on which to delete. + * @param array $identifier The deletion criteria. An associateve array containing column-value pairs. + * @return integer The number of affected rows. + */ + public function delete($tableName, array $identifier) + { + $this->connect(); + + $criteria = array(); + + foreach (array_keys($identifier) as $columnName) { + $criteria[] = $columnName . ' = ?'; + } + + $query = 'DELETE FROM ' . $tableName . ' WHERE ' . implode(' AND ', $criteria); + + return $this->executeUpdate($query, array_values($identifier)); + } + + /** + * Closes the connection. + * + * @return void + */ + public function close() + { + unset($this->_conn); + + $this->_isConnected = false; + } + + /** + * Sets the transaction isolation level. + * + * @param integer $level The level to set. + */ + public function setTransactionIsolation($level) + { + $this->_transactionIsolationLevel = $level; + + return $this->executeUpdate($this->_platform->getSetTransactionIsolationSQL($level)); + } + + /** + * Gets the currently active transaction isolation level. + * + * @return integer The current transaction isolation level. + */ + public function getTransactionIsolation() + { + return $this->_transactionIsolationLevel; + } + + /** + * Executes an SQL UPDATE statement on a table. + * + * @param string $table The name of the table to update. + * @param array $identifier The update criteria. An associative array containing column-value pairs. + * @param array $types Types of the merged $data and $identifier arrays in that order. + * @return integer The number of affected rows. + */ + public function update($tableName, array $data, array $identifier, array $types = array()) + { + $this->connect(); + $set = array(); + foreach ($data as $columnName => $value) { + $set[] = $columnName . ' = ?'; + } + + $params = array_merge(array_values($data), array_values($identifier)); + + $sql = 'UPDATE ' . $tableName . ' SET ' . implode(', ', $set) + . ' WHERE ' . implode(' = ? AND ', array_keys($identifier)) + . ' = ?'; + + return $this->executeUpdate($sql, $params, $types); + } + + /** + * Inserts a table row with specified data. + * + * @param string $table The name of the table to insert data into. + * @param array $data An associative array containing column-value pairs. + * @param array $types Types of the inserted data. + * @return integer The number of affected rows. + */ + public function insert($tableName, array $data, array $types = array()) + { + $this->connect(); + + // column names are specified as array keys + $cols = array(); + $placeholders = array(); + + foreach ($data as $columnName => $value) { + $cols[] = $columnName; + $placeholders[] = '?'; + } + + $query = 'INSERT INTO ' . $tableName + . ' (' . implode(', ', $cols) . ')' + . ' VALUES (' . implode(', ', $placeholders) . ')'; + + return $this->executeUpdate($query, array_values($data), $types); + } + + /** + * Sets the given charset on the current connection. + * + * @param string $charset The charset to set. + */ + public function setCharset($charset) + { + $this->executeUpdate($this->_platform->getSetCharsetSQL($charset)); + } + + /** + * Quote a string so it can be safely used as a table or column name, even if + * it is a reserved name. + * + * Delimiting style depends on the underlying database platform that is being used. + * + * NOTE: Just because you CAN use quoted identifiers does not mean + * you SHOULD use them. In general, they end up causing way more + * problems than they solve. + * + * @param string $str The name to be quoted. + * @return string The quoted name. + */ + public function quoteIdentifier($str) + { + return $this->_platform->quoteIdentifier($str); + } + + /** + * Quotes a given input parameter. + * + * @param mixed $input Parameter to be quoted. + * @param string $type Type of the parameter. + * @return string The quoted parameter. + */ + public function quote($input, $type = null) + { + $this->connect(); + + list($value, $bindingType) = $this->getBindingInfo($input, $type); + return $this->_conn->quote($value, $bindingType); + } + + /** + * Prepares and executes an SQL query and returns the result as an associative array. + * + * @param string $sql The SQL query. + * @param array $params The query parameters. + * @return array + */ + public function fetchAll($sql, array $params = array()) + { + return $this->executeQuery($sql, $params)->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * Prepares an SQL statement. + * + * @param string $statement The SQL statement to prepare. + * @return Doctrine\DBAL\Driver\Statement The prepared statement. + */ + public function prepare($statement) + { + $this->connect(); + + return new Statement($statement, $this); + } + + /** + * Executes an, optionally parameterized, SQL query. + * + * If the query is parameterized, a prepared statement is used. + * If an SQLLogger is configured, the execution is logged. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters to bind to the query, if any. + * @param array $types The types the previous parameters are in. + * @param QueryCacheProfile $qcp + * @return Doctrine\DBAL\Driver\Statement The executed statement. + * @internal PERF: Directly prepares a driver statement, not a wrapper. + */ + public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null) + { + if ($qcp !== null) { + return $this->executeCacheQuery($query, $params, $types, $qcp); + } + + $this->connect(); + + $hasLogger = $this->_config->getSQLLogger() !== null; + if ($hasLogger) { + $this->_config->getSQLLogger()->startQuery($query, $params, $types); + } + + if ($params) { + list($query, $params, $types) = SQLParserUtils::expandListParameters($query, $params, $types); + + $stmt = $this->_conn->prepare($query); + if ($types) { + $this->_bindTypedValues($stmt, $params, $types); + $stmt->execute(); + } else { + $stmt->execute($params); + } + } else { + $stmt = $this->_conn->query($query); + } + + if ($hasLogger) { + $this->_config->getSQLLogger()->stopQuery(); + } + + return $stmt; + } + + /** + * Execute a caching query and + * + * @param string $query + * @param array $params + * @param array $types + * @param QueryCacheProfile $qcp + * @return \Doctrine\DBAL\Driver\ResultStatement + */ + public function executeCacheQuery($query, $params, $types, QueryCacheProfile $qcp) + { + $resultCache = $qcp->getResultCacheDriver() ?: $this->_config->getResultCacheImpl(); + if (!$resultCache) { + throw CacheException::noResultDriverConfigured(); + } + + list($cacheKey, $realKey) = $qcp->generateCacheKeys($query, $params, $types); + + // fetch the row pointers entry + if ($data = $resultCache->fetch($cacheKey)) { + // is the real key part of this row pointers map or is the cache only pointing to other cache keys? + if (isset($data[$realKey])) { + return new ArrayStatement($data[$realKey]); + } else if (array_key_exists($realKey, $data)) { + return new ArrayStatement(array()); + } + } + return new ResultCacheStatement($this->executeQuery($query, $params, $types), $resultCache, $cacheKey, $realKey, $qcp->getLifetime()); + } + + /** + * Executes an, optionally parameterized, SQL query and returns the result, + * applying a given projection/transformation function on each row of the result. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters, if any. + * @param Closure $mapper The transformation function that is applied on each row. + * The function receives a single paramater, an array, that + * represents a row of the result set. + * @return mixed The projected result of the query. + */ + public function project($query, array $params, Closure $function) + { + $result = array(); + $stmt = $this->executeQuery($query, $params ?: array()); + + while ($row = $stmt->fetch()) { + $result[] = $function($row); + } + + $stmt->closeCursor(); + + return $result; + } + + /** + * Executes an SQL statement, returning a result set as a Statement object. + * + * @param string $statement + * @param integer $fetchType + * @return Doctrine\DBAL\Driver\Statement + */ + public function query() + { + $this->connect(); + + $args = func_get_args(); + + $logger = $this->getConfiguration()->getSQLLogger(); + if ($logger) { + $logger->startQuery($args[0]); + } + + $statement = call_user_func_array(array($this->_conn, 'query'), $args); + + if ($logger) { + $logger->stopQuery(); + } + + return $statement; + } + + /** + * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters + * and returns the number of affected rows. + * + * This method supports PDO binding types as well as DBAL mapping types. + * + * @param string $query The SQL query. + * @param array $params The query parameters. + * @param array $types The parameter types. + * @return integer The number of affected rows. + * @internal PERF: Directly prepares a driver statement, not a wrapper. + */ + public function executeUpdate($query, array $params = array(), array $types = array()) + { + $this->connect(); + + $hasLogger = $this->_config->getSQLLogger() !== null; + if ($hasLogger) { + $this->_config->getSQLLogger()->startQuery($query, $params, $types); + } + + if ($params) { + list($query, $params, $types) = SQLParserUtils::expandListParameters($query, $params, $types); + + $stmt = $this->_conn->prepare($query); + if ($types) { + $this->_bindTypedValues($stmt, $params, $types); + $stmt->execute(); + } else { + $stmt->execute($params); + } + $result = $stmt->rowCount(); + } else { + $result = $this->_conn->exec($query); + } + + if ($hasLogger) { + $this->_config->getSQLLogger()->stopQuery(); + } + + return $result; + } + + /** + * Execute an SQL statement and return the number of affected rows. + * + * @param string $statement + * @return integer The number of affected rows. + */ + public function exec($statement) + { + $this->connect(); + return $this->_conn->exec($statement); + } + + /** + * Returns the current transaction nesting level. + * + * @return integer The nesting level. A value of 0 means there's no active transaction. + */ + public function getTransactionNestingLevel() + { + return $this->_transactionNestingLevel; + } + + /** + * Fetch the SQLSTATE associated with the last database operation. + * + * @return integer The last error code. + */ + public function errorCode() + { + $this->connect(); + return $this->_conn->errorCode(); + } + + /** + * Fetch extended error information associated with the last database operation. + * + * @return array The last error information. + */ + public function errorInfo() + { + $this->connect(); + return $this->_conn->errorInfo(); + } + + /** + * Returns the ID of the last inserted row, or the last value from a sequence object, + * depending on the underlying driver. + * + * Note: This method may not return a meaningful or consistent result across different drivers, + * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY + * columns or sequences. + * + * @param string $seqName Name of the sequence object from which the ID should be returned. + * @return string A string representation of the last inserted ID. + */ + public function lastInsertId($seqName = null) + { + $this->connect(); + return $this->_conn->lastInsertId($seqName); + } + + /** + * Executes a function in a transaction. + * + * The function gets passed this Connection instance as an (optional) parameter. + * + * If an exception occurs during execution of the function or transaction commit, + * the transaction is rolled back and the exception re-thrown. + * + * @param Closure $func The function to execute transactionally. + */ + public function transactional(Closure $func) + { + $this->beginTransaction(); + try { + $func($this); + $this->commit(); + } catch (Exception $e) { + $this->rollback(); + throw $e; + } + } + + /** + * Set if nested transactions should use savepoints + * + * @param boolean + * @return void + */ + public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints) + { + if ($this->_transactionNestingLevel > 0) { + throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction(); + } + + if (!$this->_platform->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_nestTransactionsWithSavepoints = $nestTransactionsWithSavepoints; + } + + /** + * Get if nested transactions should use savepoints + * + * @return boolean + */ + public function getNestTransactionsWithSavepoints() + { + return $this->_nestTransactionsWithSavepoints; + } + + /** + * Returns the savepoint name to use for nested transactions are false if they are not supported + * "savepointFormat" parameter is not set + * + * @return mixed a string with the savepoint name or false + */ + protected function _getNestedTransactionSavePointName() + { + return 'DOCTRINE2_SAVEPOINT_'.$this->_transactionNestingLevel; + } + + /** + * Starts a transaction by suspending auto-commit mode. + * + * @return void + */ + public function beginTransaction() + { + $this->connect(); + + ++$this->_transactionNestingLevel; + + if ($this->_transactionNestingLevel == 1) { + $this->_conn->beginTransaction(); + } else if ($this->_nestTransactionsWithSavepoints) { + $this->createSavepoint($this->_getNestedTransactionSavePointName()); + } + } + + /** + * Commits the current transaction. + * + * @return void + * @throws ConnectionException If the commit failed due to no active transaction or + * because the transaction was marked for rollback only. + */ + public function commit() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + if ($this->_isRollbackOnly) { + throw ConnectionException::commitFailedRollbackOnly(); + } + + $this->connect(); + + if ($this->_transactionNestingLevel == 1) { + $this->_conn->commit(); + } else if ($this->_nestTransactionsWithSavepoints) { + $this->releaseSavepoint($this->_getNestedTransactionSavePointName()); + } + + --$this->_transactionNestingLevel; + } + + /** + * Cancel any database changes done during the current transaction. + * + * this method can be listened with onPreTransactionRollback and onTransactionRollback + * eventlistener methods + * + * @throws ConnectionException If the rollback operation failed. + */ + public function rollback() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + + $this->connect(); + + if ($this->_transactionNestingLevel == 1) { + $this->_transactionNestingLevel = 0; + $this->_conn->rollback(); + $this->_isRollbackOnly = false; + } else if ($this->_nestTransactionsWithSavepoints) { + $this->rollbackSavepoint($this->_getNestedTransactionSavePointName()); + --$this->_transactionNestingLevel; + } else { + $this->_isRollbackOnly = true; + --$this->_transactionNestingLevel; + } + } + + /** + * createSavepoint + * creates a new savepoint + * + * @param string $savepoint name of a savepoint to set + * @return void + */ + public function createSavepoint($savepoint) + { + if (!$this->_platform->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_conn->exec($this->_platform->createSavePoint($savepoint)); + } + + /** + * releaseSavePoint + * releases given savepoint + * + * @param string $savepoint name of a savepoint to release + * @return void + */ + public function releaseSavepoint($savepoint) + { + if (!$this->_platform->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + if ($this->_platform->supportsReleaseSavepoints()) { + $this->_conn->exec($this->_platform->releaseSavePoint($savepoint)); + } + } + + /** + * rollbackSavePoint + * releases given savepoint + * + * @param string $savepoint name of a savepoint to rollback to + * @return void + */ + public function rollbackSavepoint($savepoint) + { + if (!$this->_platform->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_conn->exec($this->_platform->rollbackSavePoint($savepoint)); + } + + /** + * Gets the wrapped driver connection. + * + * @return Doctrine\DBAL\Driver\Connection + */ + public function getWrappedConnection() + { + $this->connect(); + + return $this->_conn; + } + + /** + * Gets the SchemaManager that can be used to inspect or change the + * database schema through the connection. + * + * @return Doctrine\DBAL\Schema\AbstractSchemaManager + */ + public function getSchemaManager() + { + if ( ! $this->_schemaManager) { + $this->_schemaManager = $this->_driver->getSchemaManager($this); + } + + return $this->_schemaManager; + } + + /** + * Marks the current transaction so that the only possible + * outcome for the transaction to be rolled back. + * + * @throws ConnectionException If no transaction is active. + */ + public function setRollbackOnly() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + $this->_isRollbackOnly = true; + } + + /** + * Check whether the current transaction is marked for rollback only. + * + * @return boolean + * @throws ConnectionException If no transaction is active. + */ + public function isRollbackOnly() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + return $this->_isRollbackOnly; + } + + /** + * Converts a given value to its database representation according to the conversion + * rules of a specific DBAL mapping type. + * + * @param mixed $value The value to convert. + * @param string $type The name of the DBAL mapping type. + * @return mixed The converted value. + */ + public function convertToDatabaseValue($value, $type) + { + return Type::getType($type)->convertToDatabaseValue($value, $this->_platform); + } + + /** + * Converts a given value to its PHP representation according to the conversion + * rules of a specific DBAL mapping type. + * + * @param mixed $value The value to convert. + * @param string $type The name of the DBAL mapping type. + * @return mixed The converted type. + */ + public function convertToPHPValue($value, $type) + { + return Type::getType($type)->convertToPHPValue($value, $this->_platform); + } + + /** + * Binds a set of parameters, some or all of which are typed with a PDO binding type + * or DBAL mapping type, to a given statement. + * + * @param $stmt The statement to bind the values to. + * @param array $params The map/list of named/positional parameters. + * @param array $types The parameter types (PDO binding types or DBAL mapping types). + * @internal Duck-typing used on the $stmt parameter to support driver statements as well as + * raw PDOStatement instances. + */ + private function _bindTypedValues($stmt, array $params, array $types) + { + // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO. + if (is_int(key($params))) { + // Positional parameters + $typeOffset = array_key_exists(0, $types) ? -1 : 0; + $bindIndex = 1; + foreach ($params as $position => $value) { + $typeIndex = $bindIndex + $typeOffset; + if (isset($types[$typeIndex])) { + $type = $types[$typeIndex]; + list($value, $bindingType) = $this->getBindingInfo($value, $type); + $stmt->bindValue($bindIndex, $value, $bindingType); + } else { + $stmt->bindValue($bindIndex, $value); + } + ++$bindIndex; + } + } else { + // Named parameters + foreach ($params as $name => $value) { + if (isset($types[$name])) { + $type = $types[$name]; + list($value, $bindingType) = $this->getBindingInfo($value, $type); + $stmt->bindValue($name, $value, $bindingType); + } else { + $stmt->bindValue($name, $value); + } + } + } + } + + /** + * Gets the binding type of a given type. The given type can be a PDO or DBAL mapping type. + * + * @param mixed $value The value to bind + * @param mixed $type The type to bind (PDO or DBAL) + * @return array [0] => the (escaped) value, [1] => the binding type + */ + private function getBindingInfo($value, $type) + { + if (is_string($type)) { + $type = Type::getType($type); + } + if ($type instanceof Type) { + $value = $type->convertToDatabaseValue($value, $this->_platform); + $bindingType = $type->getBindingType(); + } else { + $bindingType = $type; // PDO::PARAM_* constants + } + return array($value, $bindingType); + } + + /** + * Create a new instance of a SQL query builder. + * + * @return Query\QueryBuilder + */ + public function createQueryBuilder() + { + return new Query\QueryBuilder($this); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/ConnectionException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/ConnectionException.php new file mode 100644 index 0000000..8c8f703 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/ConnectionException.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Doctrine\DBAL\ConnectionException + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 4628 $ + * @author Jonathan H. Wage . + */ + +namespace Doctrine\DBAL\Connections; + + +use Doctrine\DBAL\Connection, + Doctrine\DBAL\Driver, + Doctrine\DBAL\Configuration, + Doctrine\Common\EventManager, + Doctrine\DBAL\Event\ConnectionEventArgs, + Doctrine\DBAL\Events; + +/** + * Master-Slave Connection + * + * Connection can be used with master-slave setups. + * + * Important for the understanding of this connection should be how and when + * it picks the slave or master. + * + * 1. Slave if master was never picked before and ONLY if 'getWrappedConnection' + * or 'executeQuery' is used. + * 2. Master picked when 'exec', 'executeUpdate', 'insert', 'delete', 'update', 'createSavepoint', + * 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit', 'query' or + * 'prepare' is called. + * 3. If master was picked once during the lifetime of the connection it will always get picked afterwards. + * 4. One slave connection is randomly picked ONCE during a request. + * + * ATTENTION: You can write to the slave with this connection if you execute a write query without + * opening up a transaction. For example: + * + * $conn = DriverManager::getConnection(...); + * $conn->executeQuery("DELETE FROM table"); + * + * Be aware that Connection#executeQuery is a method specifically for READ + * operations only. + * + * This connection is limited to slave operations using the + * Connection#executeQuery operation only, because it wouldn't be compatible + * with the ORM or SchemaManager code otherwise. Both use all the other + * operations in a context where writes could happen to a slave, which makes + * this restricted approach necessary. + * + * You can manually connect to the master at any time by calling: + * + * $conn->connect('master'); + * + * Instantiation through the DriverManager looks like: + * + * @example + * + * $conn = DriverManager::getConnection(array( + * 'wrapperClass' => 'Doctrine\DBAL\Connections\MasterSlaveConnection', + * 'driver' => 'pdo_mysql', + * 'master' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''), + * 'slaves' => array( + * array('user' => 'slave1', 'password', 'host' => '', 'dbname' => ''), + * array('user' => 'slave2', 'password', 'host' => '', 'dbname' => ''), + * ) + * )); + * + * You can also pass 'driverOptions' and any other documented option to each of this drivers to pass additional information. + * + * @author Lars Strojny + * @author Benjamin Eberlei + */ +class MasterSlaveConnection extends Connection +{ + /** + * Master and slave connection (one of the randomly picked slaves) + * + * @var Doctrine\DBAL\Driver\Connection[] + */ + protected $connections = array('master' => null, 'slave' => null); + + /** + * Create Master Slave Connection + * + * @param array $params + * @param Driver $driver + * @param Configuration $config + * @param EventManager $eventManager + */ + public function __construct(array $params, Driver $driver, Configuration $config = null, EventManager $eventManager = null) + { + if ( !isset($params['slaves']) || !isset($params['master']) ) { + throw new \InvalidArgumentException('master or slaves configuration missing'); + } + if ( count($params['slaves']) == 0 ) { + throw new \InvalidArgumentException('You have to configure at least one slaves.'); + } + + $params['master']['driver'] = $params['driver']; + foreach ($params['slaves'] as $slaveKey => $slave) { + $params['slaves'][$slaveKey]['driver'] = $params['driver']; + } + + parent::__construct($params, $driver, $config, $eventManager); + } + + /** + * Check if the connection is currently towards the master or not. + * + * @return bool + */ + public function isConnectedToMaster() + { + return $this->_conn !== null && $this->_conn === $this->connections['master']; + } + + /** + * {@inheritDoc} + */ + public function connect($connectionName = 'slave') + { + if ( $connectionName !== 'slave' && $connectionName !== 'master' ) { + throw new \InvalidArgumentException("Invalid option to connect(), only master or slave allowed."); + } + + $forceMasterAsSlave = false; + + if ($this->getTransactionNestingLevel() > 0) { + $connectionName = 'master'; + $forceMasterAsSlave = true; + } + + if ($this->connections[$connectionName]) { + if ($forceMasterAsSlave) { + $this->connections['slave'] = $this->_conn = $this->connections['master']; + } else { + $this->_conn = $this->connections[$connectionName]; + } + return false; + } + + if ($connectionName === 'master') { + /** Set slave connection to master to avoid invalid reads */ + if ($this->connections['slave']) { + unset($this->connections['slave']); + } + + $this->connections['master'] = $this->connections['slave'] = $this->_conn = $this->connectTo($connectionName); + } else { + $this->connections['slave'] = $this->_conn = $this->connectTo($connectionName); + } + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * Connect to a specific connection + * + * @param string $connectionName + * @return Driver + */ + protected function connectTo($connectionName) + { + $params = $this->getParams(); + + $driverOptions = isset($params['driverOptions']) ? $params['driverOptions'] : array(); + + $connectionParams = $this->chooseConnectionConfiguration($connectionName, $params); + + $user = isset($connectionParams['user']) ? $connectionParams['user'] : null; + $password = isset($connectionParams['password']) ? $connectionParams['password'] : null; + + return $this->_driver->connect($connectionParams, $user, $password, $driverOptions); + } + + protected function chooseConnectionConfiguration($connectionName, $params) + { + if ($connectionName === 'master') { + return $params['master']; + } + + return $params['slaves'][array_rand($params['slaves'])]; + } + + /** + * {@inheritDoc} + */ + public function executeUpdate($query, array $params = array(), array $types = array()) + { + $this->connect('master'); + return parent::executeUpdate($query, $params, $types); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + $this->connect('master'); + return parent::beginTransaction(); + } + + /** + * {@inheritDoc} + */ + public function commit() + { + $this->connect('master'); + return parent::commit(); + } + + /** + * {@inheritDoc} + */ + public function rollback() + { + $this->connect('master'); + return parent::rollback(); + } + + /** + * {@inheritDoc} + */ + public function delete($tableName, array $identifier) + { + $this->connect('master'); + return parent::delete($tableName, $identifier); + } + + /** + * {@inheritDoc} + */ + public function update($tableName, array $data, array $identifier, array $types = array()) + { + $this->connect('master'); + return parent::update($tableName, $data, $identifier, $types); + } + + /** + * {@inheritDoc} + */ + public function insert($tableName, array $data, array $types = array()) + { + $this->connect('master'); + return parent::insert($tableName, $data, $types); + } + + /** + * {@inheritDoc} + */ + public function exec($statement) + { + $this->connect('master'); + return parent::exec($statement); + } + + /** + * {@inheritDoc} + */ + public function createSavepoint($savepoint) + { + $this->connect('master'); + + return parent::createSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function releaseSavepoint($savepoint) + { + $this->connect('master'); + + return parent::releaseSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function rollbackSavepoint($savepoint) + { + $this->connect('master'); + + return parent::rollbackSavepoint($savepoint); + } + + public function query() + { + $this->connect('master'); + + $args = func_get_args(); + + $logger = $this->getConfiguration()->getSQLLogger(); + if ($logger) { + $logger->startQuery($args[0]); + } + + $statement = call_user_func_array(array($this->_conn, 'query'), $args); + + if ($logger) { + $logger->stopQuery(); + } + + return $statement; + } + + public function prepare($statement) + { + $this->connect('master'); + + return parent::prepare($statement); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php new file mode 100644 index 0000000..9222e7e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php @@ -0,0 +1,88 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Driver interface. + * Interface that all DBAL drivers must implement. + * + * @since 2.0 + */ +interface Driver +{ + /** + * Attempts to create a connection with the database. + * + * @param array $params All connection parameters passed by the user. + * @param string $username The username to use when connecting. + * @param string $password The password to use when connecting. + * @param array $driverOptions The driver options to use when connecting. + * @return Doctrine\DBAL\Driver\Connection The database connection. + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()); + + /** + * Gets the DatabasePlatform instance that provides all the metadata about + * the platform this driver connects to. + * + * @return Doctrine\DBAL\Platforms\AbstractPlatform The database platform. + */ + public function getDatabasePlatform(); + + /** + * Gets the SchemaManager that can be used to inspect and change the underlying + * database schema of the platform this driver connects to. + * + * @param Doctrine\DBAL\Connection $conn + * @return Doctrine\DBAL\SchemaManager + */ + public function getSchemaManager(Connection $conn); + + /** + * Gets the name of the driver. + * + * @return string The name of the driver. + */ + public function getName(); + + /** + * Get the name of the database connected to for this driver. + * + * @param Doctrine\DBAL\Connection $conn + * @return string $database + */ + public function getDatabase(Connection $conn); +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php new file mode 100644 index 0000000..c3f0d56 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Connection interface. + * Driver connections must implement this interface. + * + * This resembles (a subset of) the PDO interface. + * + * @since 2.0 + */ +interface Connection +{ + function prepare($prepareString); + function query(); + function quote($input, $type=\PDO::PARAM_STR); + function exec($statement); + function lastInsertId($name = null); + function beginTransaction(); + function commit(); + function rollBack(); + function errorCode(); + function errorInfo(); +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php new file mode 100644 index 0000000..c9d2fef --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php @@ -0,0 +1,115 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +class DB2Connection implements \Doctrine\DBAL\Driver\Connection +{ + private $_conn = null; + + public function __construct(array $params, $username, $password, $driverOptions = array()) + { + $isPersistant = (isset($params['persistent']) && $params['persistent'] == true); + + if ($isPersistant) { + $this->_conn = db2_pconnect($params['dbname'], $username, $password, $driverOptions); + } else { + $this->_conn = db2_connect($params['dbname'], $username, $password, $driverOptions); + } + if (!$this->_conn) { + throw new DB2Exception(db2_conn_errormsg()); + } + } + + public function prepare($sql) + { + $stmt = @db2_prepare($this->_conn, $sql); + if (!$stmt) { + throw new DB2Exception(db2_stmt_errormsg()); + } + return new DB2Statement($stmt); + } + + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + $stmt = $this->prepare($sql); + $stmt->execute(); + return $stmt; + } + + public function quote($input, $type=\PDO::PARAM_STR) + { + $input = db2_escape_string($input); + if ($type == \PDO::PARAM_INT ) { + return $input; + } else { + return "'".$input."'"; + } + } + + public function exec($statement) + { + $stmt = $this->prepare($statement); + $stmt->execute(); + return $stmt->rowCount(); + } + + public function lastInsertId($name = null) + { + return db2_last_insert_id($this->_conn); + } + + public function beginTransaction() + { + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_OFF); + } + + public function commit() + { + if (!db2_commit($this->_conn)) { + throw new DB2Exception(db2_conn_errormsg($this->_conn)); + } + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); + } + + public function rollBack() + { + if (!db2_rollback($this->_conn)) { + throw new DB2Exception(db2_conn_errormsg($this->_conn)); + } + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); + } + + public function errorCode() + { + return db2_conn_error($this->_conn); + } + + public function errorInfo() + { + return array( + 0 => db2_conn_errormsg($this->_conn), + 1 => $this->errorCode(), + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php new file mode 100644 index 0000000..d574b68 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php @@ -0,0 +1,111 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use Doctrine\DBAL\Driver, + Doctrine\DBAL\Connection; + +/** + * IBM DB2 Driver + * + * @since 2.0 + * @author Benjamin Eberlei + */ +class DB2Driver implements Driver +{ + /** + * Attempts to create a connection with the database. + * + * @param array $params All connection parameters passed by the user. + * @param string $username The username to use when connecting. + * @param string $password The password to use when connecting. + * @param array $driverOptions The driver options to use when connecting. + * @return Doctrine\DBAL\Driver\Connection The database connection. + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + if ( ! isset($params['protocol'])) { + $params['protocol'] = 'TCPIP'; + } + + if ($params['host'] !== 'localhost' && $params['host'] != '127.0.0.1') { + // if the host isn't localhost, use extended connection params + $params['dbname'] = 'DRIVER={IBM DB2 ODBC DRIVER}' . + ';DATABASE=' . $params['dbname'] . + ';HOSTNAME=' . $params['host'] . + ';PROTOCOL=' . $params['protocol'] . + ';UID=' . $username . + ';PWD=' . $password .';'; + if (isset($params['port'])) { + $params['dbname'] .= 'PORT=' . $params['port']; + } + + $username = null; + $password = null; + } + + return new DB2Connection($params, $username, $password, $driverOptions); + } + + /** + * Gets the DatabasePlatform instance that provides all the metadata about + * the platform this driver connects to. + * + * @return Doctrine\DBAL\Platforms\AbstractPlatform The database platform. + */ + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\DB2Platform; + } + + /** + * Gets the SchemaManager that can be used to inspect and change the underlying + * database schema of the platform this driver connects to. + * + * @param Doctrine\DBAL\Connection $conn + * @return Doctrine\DBAL\SchemaManager + */ + public function getSchemaManager(Connection $conn) + { + return new \Doctrine\DBAL\Schema\DB2SchemaManager($conn); + } + + /** + * Gets the name of the driver. + * + * @return string The name of the driver. + */ + public function getName() + { + return 'ibm_db2'; + } + + /** + * Get the name of the database connected to for this driver. + * + * @param Doctrine\DBAL\Connection $conn + * @return string $database + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php new file mode 100644 index 0000000..7a6cc41 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php @@ -0,0 +1,27 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +class DB2Exception extends \Exception +{ + +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php new file mode 100644 index 0000000..27da2ee --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php @@ -0,0 +1,215 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use \Doctrine\DBAL\Driver\Statement; + +class DB2Statement implements \IteratorAggregate, Statement +{ + private $_stmt = null; + + private $_bindParam = array(); + + private $_defaultFetchStyle = \PDO::FETCH_BOTH; + + /** + * DB2_BINARY, DB2_CHAR, DB2_DOUBLE, or DB2_LONG + * @var array + */ + static private $_typeMap = array( + \PDO::PARAM_INT => DB2_LONG, + \PDO::PARAM_STR => DB2_CHAR, + ); + + public function __construct($stmt) + { + $this->_stmt = $stmt; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $value, $type); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null) + { + $this->_bindParam[$column] =& $variable; + + if ($type && isset(self::$_typeMap[$type])) { + $type = self::$_typeMap[$type]; + } else { + $type = DB2_CHAR; + } + + if (!db2_bind_param($this->_stmt, $column, "variable", DB2_PARAM_IN, $type)) { + throw new DB2Exception(db2_stmt_errormsg()); + } + return true; + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + if (!$this->_stmt) { + return false; + } + + $this->_bindParam = array(); + db2_free_result($this->_stmt); + $ret = db2_free_stmt($this->_stmt); + $this->_stmt = false; + return $ret; + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + if (!$this->_stmt) { + return false; + } + return db2_num_fields($this->_stmt); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return db2_stmt_error(); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return array( + 0 => db2_stmt_errormsg(), + 1 => db2_stmt_error(), + ); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if (!$this->_stmt) { + return false; + } + + /*$retval = true; + if ($params !== null) { + $retval = @db2_execute($this->_stmt, $params); + } else { + $retval = @db2_execute($this->_stmt); + }*/ + if ($params === null) { + ksort($this->_bindParam); + $params = array_values($this->_bindParam); + } + $retval = @db2_execute($this->_stmt, $params); + + if ($retval === false) { + throw new DB2Exception(db2_stmt_errormsg()); + } + return $retval; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchStyle = \PDO::FETCH_BOTH, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchStyle = $fetchStyle; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll($this->_defaultFetchStyle); + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchStyle = null) + { + $fetchStyle = $fetchStyle ?: $this->_defaultFetchStyle; + switch ($fetchStyle) { + case \PDO::FETCH_BOTH: + return db2_fetch_both($this->_stmt); + case \PDO::FETCH_ASSOC: + return db2_fetch_assoc($this->_stmt); + case \PDO::FETCH_NUM: + return db2_fetch_array($this->_stmt); + default: + throw new DB2Exception("Given Fetch-Style " . $fetchStyle . " is not supported."); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchStyle = null) + { + $fetchStyle = $fetchStyle ?: $this->_defaultFetchStyle; + $rows = array(); + while ($row = $this->fetch($fetchStyle)) { + $rows[] = $row; + } + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(\PDO::FETCH_NUM); + if ($row && isset($row[$columnIndex])) { + return $row[$columnIndex]; + } + return false; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return (@db2_num_rows($this->_stmt))?:0; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Driver.php new file mode 100644 index 0000000..b77de67 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Driver.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver as DriverInterface; + +/** + * @author Kim Hemsø Rasmussen + */ +class Driver implements DriverInterface +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + return new MysqliConnection($params, $username, $password, $driverOptions); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'mysqli'; + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\MySqlSchemaManager($conn); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\MySqlPlatform(); + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php new file mode 100644 index 0000000..b69535b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php @@ -0,0 +1,146 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\Connection as Connection; + +/** + * @author Kim Hemsø Rasmussen + */ +class MysqliConnection implements Connection +{ + /** + * @var \mysqli + */ + private $_conn; + + public function __construct(array $params, $username, $password, array $driverOptions = array()) + { + $port = isset($params['port']) ? $params['port'] : ini_get('mysqli.default_port'); + $socket = isset($params['unix_socket']) ? $params['unix_socket'] : ini_get('mysqli.default_socket'); + + $this->_conn = mysqli_init(); + if (!$this->_conn->real_connect($params['host'], $username, $password, $params['dbname'], $port, $socket)) { + throw new MysqliException($this->_conn->connect_error, $this->_conn->connect_errno); + } + + if (isset($params['charset'])) { + $this->_conn->set_charset($params['charset']); + } + } + + /** + * Retrieve mysqli native resource handle. + * + * Could be used if part of your application is not using DBAL + * + * @return mysqli + */ + public function getWrappedResourceHandle() + { + return $this->_conn; + } + + /** + * {@inheritdoc} + */ + public function prepare($prepareString) + { + return new MysqliStatement($this->_conn, $prepareString); + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + $stmt = $this->prepare($sql); + $stmt->execute(); + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function quote($input, $type=\PDO::PARAM_STR) + { + return "'". $this->_conn->escape_string($input) ."'"; + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + $this->_conn->query($statement); + return $this->_conn->affected_rows; + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + return $this->_conn->insert_id; + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + $this->_conn->query('START TRANSACTION'); + return true; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->_conn->commit(); + } + + /** + * {@inheritdoc}non-PHPdoc) + */ + public function rollBack() + { + return $this->_conn->rollback(); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return $this->_conn->errno; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return $this->_conn->error; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php new file mode 100644 index 0000000..e59a803 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php @@ -0,0 +1,26 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\Mysqli; + +/** + * @author Kim Hemsø Rasmussen + */ +class MysqliException extends \Exception +{} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php new file mode 100644 index 0000000..d7f9703 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php @@ -0,0 +1,335 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\Statement; +use PDO; + +/** + * @author Kim Hemsø Rasmussen + */ +class MysqliStatement implements \IteratorAggregate, Statement +{ + protected static $_paramTypeMap = array( + PDO::PARAM_STR => 's', + PDO::PARAM_BOOL => 'i', + PDO::PARAM_NULL => 's', + PDO::PARAM_INT => 'i', + PDO::PARAM_LOB => 's' // TODO Support LOB bigger then max package size. + ); + + protected $_conn; + protected $_stmt; + + /** + * @var null|false|array + */ + protected $_columnNames; + + /** + * @var null|array + */ + protected $_rowBindedValues; + + /** + * @var array + */ + protected $_bindedValues; + + /** + * Contains ref values for bindValue() + * + * @var array + */ + protected $_values = array(); + + protected $_defaultFetchStyle = PDO::FETCH_BOTH; + + public function __construct(\mysqli $conn, $prepareString) + { + $this->_conn = $conn; + $this->_stmt = $conn->prepare($prepareString); + if (false === $this->_stmt) { + throw new MysqliException($this->_conn->error, $this->_conn->errno); + } + + $paramCount = $this->_stmt->param_count; + if (0 < $paramCount) { + // Index 0 is types + // Need to init the string else php think we are trying to access it as a array. + $bindedValues = array(0 => str_repeat('s', $paramCount)); + $null = null; + for ($i = 1; $i < $paramCount; $i++) { + $bindedValues[] =& $null; + } + $this->_bindedValues = $bindedValues; + } + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null) + { + if (null === $type) { + $type = 's'; + } else { + if (isset(self::$_paramTypeMap[$type])) { + $type = self::$_paramTypeMap[$type]; + } else { + throw new MysqliException("Unkown type: '{$type}'"); + } + } + + $this->_bindedValues[$column] =& $variable; + $this->_bindedValues[0][$column - 1] = 's'; + return true; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + if (null === $type) { + $type = 's'; + } else { + if (isset(self::$_paramTypeMap[$type])) { + $type = self::$_paramTypeMap[$type]; + } else { + throw new MysqliException("Unknown type: '{$type}'"); + } + } + + $this->_values[$param] = $value; + $this->_bindedValues[$param] =& $this->_values[$param]; + $this->_bindedValues[0][$param - 1] = 's'; + return true; + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if (null !== $this->_bindedValues) { + if (null !== $params) { + if (!$this->_bindValues($params)) { + throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + } + } else { + if (!call_user_func_array(array($this->_stmt, 'bind_param'), $this->_bindedValues)) { + throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + } + } + } + + if (!$this->_stmt->execute()) { + throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + } + + if (null === $this->_columnNames) { + $meta = $this->_stmt->result_metadata(); + if (false !== $meta) { + $columnNames = array(); + foreach ($meta->fetch_fields() as $col) { + $columnNames[] = $col->name; + } + $meta->free(); + + $this->_columnNames = $columnNames; + $this->_rowBindedValues = array_fill(0, count($columnNames), NULL); + + $refs = array(); + foreach ($this->_rowBindedValues as $key => &$value) { + $refs[$key] =& $value; + } + + if (!call_user_func_array(array($this->_stmt, 'bind_result'), $refs)) { + throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + } + } else { + $this->_columnNames = false; + } + } + + // We have a result. + if (false !== $this->_columnNames) { + $this->_stmt->store_result(); + } + return true; + } + + /** + * Bind a array of values to bound parameters + * + * @param array $values + * @return boolean + */ + private function _bindValues($values) + { + $params = array(); + $types = str_repeat('s', count($values)); + $params[0] = $types; + + foreach ($values as &$v) { + $params[] =& $v; + } + return call_user_func_array(array($this->_stmt, 'bind_param'), $params); + } + + /** + * @return null|false|array + */ + private function _fetch() + { + $ret = $this->_stmt->fetch(); + + if (true === $ret) { + $values = array(); + foreach ($this->_rowBindedValues as $v) { + // Mysqli converts them to a scalar type it can fit in. + $values[] = null === $v ? null : (string)$v; + } + return $values; + } + return $ret; + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchStyle = null) + { + $values = $this->_fetch(); + if (null === $values) { + return null; + } + + if (false === $values) { + throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + } + + $fetchStyle = $fetchStyle ?: $this->_defaultFetchStyle; + + switch ($fetchStyle) { + case PDO::FETCH_NUM: + return $values; + + case PDO::FETCH_ASSOC: + return array_combine($this->_columnNames, $values); + + case PDO::FETCH_BOTH: + $ret = array_combine($this->_columnNames, $values); + $ret += $values; + return $ret; + + default: + throw new MysqliException("Unknown fetch type '{$fetchStyle}'"); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchStyle = null) + { + $fetchStyle = $fetchStyle ?: $this->_defaultFetchStyle; + + $a = array(); + while (($row = $this->fetch($fetchStyle)) !== null) { + $a[] = $row; + } + return $a; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + if (null === $row) { + return false; + } + return $row[$columnIndex]; + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return $this->_stmt->errno; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return $this->_stmt->error; + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + $this->_stmt->free_result(); + return true; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + if (false === $this->_columnNames) { + return $this->_stmt->affected_rows; + } + return $this->_stmt->num_rows; + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->_stmt->field_count; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode = PDO::FETCH_BOTH, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchStyle = $fetchMode; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll($this->_defaultFetchStyle); + return new \ArrayIterator($data); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Driver.php new file mode 100644 index 0000000..650ad9b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Driver.php @@ -0,0 +1,95 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use Doctrine\DBAL\Platforms; + +/** + * A Doctrine DBAL driver for the Oracle OCI8 PHP extensions. + * + * @author Roman Borschel + * @since 2.0 + */ +class Driver implements \Doctrine\DBAL\Driver +{ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + return new OCI8Connection( + $username, + $password, + $this->_constructDsn($params), + isset($params['charset']) ? $params['charset'] : null, + isset($params['sessionMode']) ? $params['sessionMode'] : OCI_DEFAULT + ); + } + + /** + * Constructs the Oracle DSN. + * + * @return string The DSN. + */ + protected function _constructDsn(array $params) + { + $dsn = ''; + if (isset($params['host']) && $params['host'] != '') { + $dsn .= '(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)' . + '(HOST=' . $params['host'] . ')'; + + if (isset($params['port'])) { + $dsn .= '(PORT=' . $params['port'] . ')'; + } else { + $dsn .= '(PORT=1521)'; + } + + if (isset($params['service']) && $params['service'] == true) { + $dsn .= '))(CONNECT_DATA=(SERVICE_NAME=' . $params['dbname'] . ')))'; + } else { + $dsn .= '))(CONNECT_DATA=(SID=' . $params['dbname'] . ')))'; + } + } else { + $dsn .= $params['dbname']; + } + + return $dsn; + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\OraclePlatform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\OracleSchemaManager($conn); + } + + public function getName() + { + return 'oci8'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['user']; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php new file mode 100644 index 0000000..f00ffaf --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php @@ -0,0 +1,172 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +/** + * OCI8 implementation of the Connection interface. + * + * @since 2.0 + */ +class OCI8Connection implements \Doctrine\DBAL\Driver\Connection +{ + protected $_dbh; + + protected $_executeMode = OCI_COMMIT_ON_SUCCESS; + + /** + * Create a Connection to an Oracle Database using oci8 extension. + * + * @param string $username + * @param string $password + * @param string $db + */ + public function __construct($username, $password, $db, $charset = null, $sessionMode = OCI_DEFAULT) + { + if (!defined('OCI_NO_AUTO_COMMIT')) { + define('OCI_NO_AUTO_COMMIT', 0); + } + + $this->_dbh = @oci_connect($username, $password, $db, $charset, $sessionMode); + if (!$this->_dbh) { + throw OCI8Exception::fromErrorInfo(oci_error()); + } + } + + /** + * Create a non-executed prepared statement. + * + * @param string $prepareString + * @return OCI8Statement + */ + public function prepare($prepareString) + { + return new OCI8Statement($this->_dbh, $prepareString, $this); + } + + /** + * @param string $sql + * @return OCI8Statement + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + //$fetchMode = $args[1]; + $stmt = $this->prepare($sql); + $stmt->execute(); + return $stmt; + } + + /** + * Quote input value. + * + * @param mixed $input + * @param int $type PDO::PARAM* + * @return mixed + */ + public function quote($value, $type=\PDO::PARAM_STR) + { + if (is_int($value) || is_float($value)) { + return $value; + } + $value = str_replace("'", "''", $value); + return "'" . addcslashes($value, "\000\n\r\\\032") . "'"; + } + + /** + * + * @param string $statement + * @return int + */ + public function exec($statement) + { + $stmt = $this->prepare($statement); + $stmt->execute(); + return $stmt->rowCount(); + } + + public function lastInsertId($name = null) + { + //TODO: throw exception or support sequences? + } + + /** + * Return the current execution mode. + */ + public function getExecuteMode() + { + return $this->_executeMode; + } + + /** + * Start a transactiom + * + * Oracle has to explicitly set the autocommit mode off. That means + * after connection, a commit or rollback there is always automatically + * opened a new transaction. + * + * @return bool + */ + public function beginTransaction() + { + $this->_executeMode = OCI_NO_AUTO_COMMIT; + return true; + } + + /** + * @throws OCI8Exception + * @return bool + */ + public function commit() + { + if (!oci_commit($this->_dbh)) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + $this->_executeMode = OCI_COMMIT_ON_SUCCESS; + return true; + } + + /** + * @throws OCI8Exception + * @return bool + */ + public function rollBack() + { + if (!oci_rollback($this->_dbh)) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + $this->_executeMode = OCI_COMMIT_ON_SUCCESS; + return true; + } + + public function errorCode() + { + $error = oci_error($this->_dbh); + if ($error !== false) { + $error = $error['code']; + } + return $error; + } + + public function errorInfo() + { + return oci_error($this->_dbh); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php new file mode 100644 index 0000000..66fe615 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php @@ -0,0 +1,30 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\OCI8; + +class OCI8Exception extends \Exception +{ + static public function fromErrorInfo($error) + { + return new self($error['message'], $error['code']); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php new file mode 100644 index 0000000..677b684 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php @@ -0,0 +1,258 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use PDO; +use IteratorAggregate; +use Doctrine\DBAL\Driver\Statement; + +/** + * The OCI8 implementation of the Statement interface. + * + * @since 2.0 + * @author Roman Borschel + */ +class OCI8Statement implements \IteratorAggregate, Statement +{ + /** Statement handle. */ + protected $_dbh; + protected $_sth; + protected $_conn; + protected static $_PARAM = ':param'; + protected static $fetchStyleMap = array( + PDO::FETCH_BOTH => OCI_BOTH, + PDO::FETCH_ASSOC => OCI_ASSOC, + PDO::FETCH_NUM => OCI_NUM, + PDO::PARAM_LOB => OCI_B_BLOB, + ); + protected $_defaultFetchStyle = PDO::FETCH_BOTH; + protected $_paramMap = array(); + + /** + * Creates a new OCI8Statement that uses the given connection handle and SQL statement. + * + * @param resource $dbh The connection handle. + * @param string $statement The SQL statement. + */ + public function __construct($dbh, $statement, OCI8Connection $conn) + { + list($statement, $paramMap) = self::convertPositionalToNamedPlaceholders($statement); + $this->_sth = oci_parse($dbh, $statement); + $this->_dbh = $dbh; + $this->_paramMap = $paramMap; + $this->_conn = $conn; + } + + /** + * Convert positional (?) into named placeholders (:param) + * + * Oracle does not support positional parameters, hence this method converts all + * positional parameters into artificially named parameters. Note that this conversion + * is not perfect. All question marks (?) in the original statement are treated as + * placeholders and converted to a named parameter. + * + * The algorithm uses a state machine with two possible states: InLiteral and NotInLiteral. + * Question marks inside literal strings are therefore handled correctly by this method. + * This comes at a cost, the whole sql statement has to be looped over. + * + * @todo extract into utility class in Doctrine\DBAL\Util namespace + * @todo review and test for lost spaces. we experienced missing spaces with oci8 in some sql statements. + * @param string $statement The SQL statement to convert. + * @return string + */ + static public function convertPositionalToNamedPlaceholders($statement) + { + $count = 1; + $inLiteral = false; // a valid query never starts with quotes + $stmtLen = strlen($statement); + $paramMap = array(); + for ($i = 0; $i < $stmtLen; $i++) { + if ($statement[$i] == '?' && !$inLiteral) { + // real positional parameter detected + $paramMap[$count] = ":param$count"; + $len = strlen($paramMap[$count]); + $statement = substr_replace($statement, ":param$count", $i, 1); + $i += $len-1; // jump ahead + $stmtLen = strlen($statement); // adjust statement length + ++$count; + } else if ($statement[$i] == "'" || $statement[$i] == '"') { + $inLiteral = ! $inLiteral; // switch state! + } + } + + return array($statement, $paramMap); + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $value, $type); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null) + { + $column = isset($this->_paramMap[$column]) ? $this->_paramMap[$column] : $column; + + if ($type == \PDO::PARAM_LOB) { + $lob = oci_new_descriptor($this->_dbh, OCI_D_LOB); + $lob->writeTemporary($variable, OCI_TEMP_BLOB); + + return oci_bind_by_name($this->_sth, $column, $lob, -1, OCI_B_BLOB); + } else { + return oci_bind_by_name($this->_sth, $column, $variable); + } + } + + /** + * Closes the cursor, enabling the statement to be executed again. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + public function closeCursor() + { + return oci_free_statement($this->_sth); + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return oci_num_fields($this->_sth); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + $error = oci_error($this->_sth); + if ($error !== false) { + $error = $error['code']; + } + return $error; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return oci_error($this->_sth); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if ($params) { + $hasZeroIndex = array_key_exists(0, $params); + foreach ($params as $key => $val) { + if ($hasZeroIndex && is_numeric($key)) { + $this->bindValue($key + 1, $val); + } else { + $this->bindValue($key, $val); + } + } + } + + $ret = @oci_execute($this->_sth, $this->_conn->getExecuteMode()); + if ( ! $ret) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + return $ret; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchStyle = PDO::FETCH_BOTH, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchStyle = $fetchStyle; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll($this->_defaultFetchStyle); + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchStyle = null) + { + $fetchStyle = $fetchStyle ?: $this->_defaultFetchStyle; + if ( ! isset(self::$fetchStyleMap[$fetchStyle])) { + throw new \InvalidArgumentException("Invalid fetch style: " . $fetchStyle); + } + + return oci_fetch_array($this->_sth, self::$fetchStyleMap[$fetchStyle] | OCI_RETURN_NULLS | OCI_RETURN_LOBS); + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchStyle = null) + { + $fetchStyle = $fetchStyle ?: $this->_defaultFetchStyle; + if ( ! isset(self::$fetchStyleMap[$fetchStyle])) { + throw new \InvalidArgumentException("Invalid fetch style: " . $fetchStyle); + } + + $result = array(); + if (self::$fetchStyleMap[$fetchStyle] === OCI_BOTH) { + while ($row = $this->fetch($fetchStyle)) { + $result[] = $row; + } + } else { + oci_fetch_all($this->_sth, $result, 0, -1, + self::$fetchStyleMap[$fetchStyle] | OCI_RETURN_NULLS | OCI_FETCHSTATEMENT_BY_ROW | OCI_RETURN_LOBS); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = oci_fetch_array($this->_sth, OCI_NUM | OCI_RETURN_NULLS | OCI_RETURN_LOBS); + return isset($row[$columnIndex]) ? $row[$columnIndex] : false; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return oci_num_rows($this->_sth); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php new file mode 100644 index 0000000..f006807 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use \PDO; + +/** + * PDO implementation of the Connection interface. + * Used by all PDO-based drivers. + * + * @since 2.0 + */ +class PDOConnection extends PDO implements Connection +{ + public function __construct($dsn, $user = null, $password = null, array $options = null) + { + parent::__construct($dsn, $user, $password, $options); + $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('Doctrine\DBAL\Driver\PDOStatement', array())); + $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php new file mode 100644 index 0000000..844f2ab --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php @@ -0,0 +1,126 @@ +. +*/ + +namespace Doctrine\DBAL\Driver\PDOIbm; + +use Doctrine\DBAL\Connection; + +/** + * Driver for the PDO IBM extension + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Driver implements \Doctrine\DBAL\Driver +{ + /** + * Attempts to establish a connection with the underlying driver. + * + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * @return Doctrine\DBAL\Driver\Connection + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + $conn = new \Doctrine\DBAL\Driver\PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + return $conn; + } + + /** + * Constructs the MySql PDO DSN. + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'ibm:'; + if (isset($params['host'])) { + $dsn .= 'HOSTNAME=' . $params['host'] . ';'; + } + if (isset($params['port'])) { + $dsn .= 'PORT=' . $params['port'] . ';'; + } + $dsn .= 'PROTOCOL=TCPIP;'; + if (isset($params['dbname'])) { + $dsn .= 'DATABASE=' . $params['dbname'] . ';'; + } + + return $dsn; + } + + /** + * Gets the DatabasePlatform instance that provides all the metadata about + * the platform this driver connects to. + * + * @return Doctrine\DBAL\Platforms\AbstractPlatform The database platform. + */ + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\DB2Platform; + } + + /** + * Gets the SchemaManager that can be used to inspect and change the underlying + * database schema of the platform this driver connects to. + * + * @param Doctrine\DBAL\Connection $conn + * @return Doctrine\DBAL\SchemaManager + */ + public function getSchemaManager(Connection $conn) + { + return new \Doctrine\DBAL\Schema\DB2SchemaManager($conn); + } + + /** + * Gets the name of the driver. + * + * @return string The name of the driver. + */ + public function getName() + { + return 'pdo_ibm'; + } + + /** + * Get the name of the database connected to for this driver. + * + * @param Doctrine\DBAL\Connection $conn + * @return string $database + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php new file mode 100644 index 0000000..cb2df85 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php @@ -0,0 +1,102 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOMySql; + +use Doctrine\DBAL\Connection; + +/** + * PDO MySql driver. + * + * @since 2.0 + */ +class Driver implements \Doctrine\DBAL\Driver +{ + /** + * Attempts to establish a connection with the underlying driver. + * + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * @return Doctrine\DBAL\Driver\Connection + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + $conn = new \Doctrine\DBAL\Driver\PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + return $conn; + } + + /** + * Constructs the MySql PDO DSN. + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'mysql:'; + if (isset($params['host']) && $params['host'] != '') { + $dsn .= 'host=' . $params['host'] . ';'; + } + if (isset($params['port'])) { + $dsn .= 'port=' . $params['port'] . ';'; + } + if (isset($params['dbname'])) { + $dsn .= 'dbname=' . $params['dbname'] . ';'; + } + if (isset($params['unix_socket'])) { + $dsn .= 'unix_socket=' . $params['unix_socket'] . ';'; + } + if (isset($params['charset'])) { + $dsn .= 'charset=' . $params['charset'] . ';'; + } + + return $dsn; + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\MySqlPlatform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\MySqlSchemaManager($conn); + } + + public function getName() + { + return 'pdo_mysql'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + if (isset($params['dbname'])) { + return $params['dbname']; + } + return $conn->query('SELECT DATABASE()')->fetchColumn(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php new file mode 100644 index 0000000..1f007c1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOOracle; + +use Doctrine\DBAL\Platforms; + +/** + * PDO Oracle driver + * + * WARNING: This driver gives us segfauls in our testsuites on CLOB and other + * stuff. PDO Oracle is not maintained by Oracle or anyone in the PHP community, + * which leads us to the recommendation to use the "oci8" driver to connect + * to Oracle instead. + */ +class Driver implements \Doctrine\DBAL\Driver +{ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + return new \Doctrine\DBAL\Driver\PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } + + /** + * Constructs the Oracle PDO DSN. + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'oci:'; + if (isset($params['host'])) { + $dsn .= 'dbname=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)' . + '(HOST=' . $params['host'] . ')'; + + if (isset($params['port'])) { + $dsn .= '(PORT=' . $params['port'] . ')'; + } else { + $dsn .= '(PORT=1521)'; + } + + if (isset($params['service']) && $params['service'] == true) { + $dsn .= '))(CONNECT_DATA=(SERVICE_NAME=' . $params['dbname'] . ')))'; + } else { + $dsn .= '))(CONNECT_DATA=(SID=' . $params['dbname'] . ')))'; + } + } else { + $dsn .= 'dbname=' . $params['dbname']; + } + + if (isset($params['charset'])) { + $dsn .= ';charset=' . $params['charset']; + } + + return $dsn; + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\OraclePlatform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\OracleSchemaManager($conn); + } + + public function getName() + { + return 'pdo_oracle'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['user']; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php new file mode 100644 index 0000000..247eb8f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php @@ -0,0 +1,70 @@ +_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } + + /** + * Constructs the Postgres PDO DSN. + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'pgsql:'; + if (isset($params['host']) && $params['host'] != '') { + $dsn .= 'host=' . $params['host'] . ' '; + } + if (isset($params['port']) && $params['port'] != '') { + $dsn .= 'port=' . $params['port'] . ' '; + } + if (isset($params['dbname'])) { + $dsn .= 'dbname=' . $params['dbname'] . ' '; + } + + return $dsn; + } + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\PostgreSqlPlatform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\PostgreSqlSchemaManager($conn); + } + + public function getName() + { + return 'pdo_pgsql'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php new file mode 100644 index 0000000..e13526a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php @@ -0,0 +1,116 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlite; + +/** + * The PDO Sqlite driver. + * + * @since 2.0 + */ +class Driver implements \Doctrine\DBAL\Driver +{ + /** + * @var array + */ + protected $_userDefinedFunctions = array( + 'sqrt' => array('callback' => array('Doctrine\DBAL\Platforms\SqlitePlatform', 'udfSqrt'), 'numArgs' => 1), + 'mod' => array('callback' => array('Doctrine\DBAL\Platforms\SqlitePlatform', 'udfMod'), 'numArgs' => 2), + 'locate' => array('callback' => array('Doctrine\DBAL\Platforms\SqlitePlatform', 'udfLocate'), 'numArgs' => -1), + ); + + /** + * Tries to establish a database connection to SQLite. + * + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * @return Connection + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + if (isset($driverOptions['userDefinedFunctions'])) { + $this->_userDefinedFunctions = array_merge( + $this->_userDefinedFunctions, $driverOptions['userDefinedFunctions']); + unset($driverOptions['userDefinedFunctions']); + } + + $pdo = new \Doctrine\DBAL\Driver\PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + + foreach ($this->_userDefinedFunctions AS $fn => $data) { + $pdo->sqliteCreateFunction($fn, $data['callback'], $data['numArgs']); + } + + return $pdo; + } + + /** + * Constructs the Sqlite PDO DSN. + * + * @return string The DSN. + * @override + */ + protected function _constructPdoDsn(array $params) + { + $dsn = 'sqlite:'; + if (isset($params['path'])) { + $dsn .= $params['path']; + } else if (isset($params['memory'])) { + $dsn .= ':memory:'; + } + + return $dsn; + } + + /** + * Gets the database platform that is relevant for this driver. + */ + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\SqlitePlatform(); + } + + /** + * Gets the schema manager that is relevant for this driver. + * + * @param Doctrine\DBAL\Connection $conn + * @return Doctrine\DBAL\Schema\SqliteSchemaManager + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\SqliteSchemaManager($conn); + } + + public function getName() + { + return 'pdo_sqlite'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return isset($params['path']) ? $params['path'] : null; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php new file mode 100644 index 0000000..ba8aaf0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlsrv; + +/** + * Sqlsrv Connection implementation. + * + * @since 2.0 + */ +class Connection extends \Doctrine\DBAL\Driver\PDOConnection implements \Doctrine\DBAL\Driver\Connection +{ + /** + * @override + */ + public function quote($value, $type=\PDO::PARAM_STR) + { + $val = parent::quote($value, $type); + + // Fix for a driver version terminating all values with null byte + if (strpos($val, "\0") !== false) { + $val = substr($val, 0, -1); + } + + return $val; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php new file mode 100644 index 0000000..d3c6bf1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php @@ -0,0 +1,88 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlsrv; + +/** + * The PDO-based Sqlsrv driver. + * + * @since 2.0 + */ +class Driver implements \Doctrine\DBAL\Driver +{ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + return new Connection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } + + /** + * Constructs the Sqlsrv PDO DSN. + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'sqlsrv:server='; + + if (isset($params['host'])) { + $dsn .= $params['host']; + } + + if (isset($params['port']) && !empty($params['port'])) { + $dsn .= ',' . $params['port']; + } + + if (isset($params['dbname'])) {; + $dsn .= ';Database=' . $params['dbname']; + } + + if (isset($params['MultipleActiveResultSets'])) { + $dsn .= '; MultipleActiveResultSets=' . ($params['MultipleActiveResultSets'] ? 'true' : 'false'); + } + + return $dsn; + } + + + public function getDatabasePlatform() + { + return new \Doctrine\DBAL\Platforms\SQLServer2008Platform(); + } + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new \Doctrine\DBAL\Schema\SQLServerSchemaManager($conn); + } + + public function getName() + { + return 'pdo_sqlsrv'; + } + + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php new file mode 100644 index 0000000..6456af7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * The PDO implementation of the Statement interface. + * Used by all PDO-based drivers. + * + * @since 2.0 + */ +class PDOStatement extends \PDOStatement implements Statement +{ + private function __construct() {} + + public function setFetchMode($fetchStyle, $arg2 = null, $arg3 = null) + { + // This thin wrapper is necessary to shield against the weird signature + // of PDOStatement::setFetchMode(): even if the second and third + // parameters are optional, PHP will not let us remove it from this + // declaration. + if ($arg2 === null && $arg3 === null) { + return parent::setFetchMode($fetchStyle); + } + + if ($arg3 === null) { + return parent::setFetchMode($fetchStyle, $arg2); + } + + return parent::setFetchMode($fetchStyle, $arg2, $arg3); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php new file mode 100644 index 0000000..ef5eb02 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php @@ -0,0 +1,113 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use PDO; + +/** + * Interface for the reading part of a prepare statement only. + * + * @author Benjamin Eberlei + */ +interface ResultStatement extends \Traversable +{ + /** + * Closes the cursor, enabling the statement to be executed again. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function closeCursor(); + + + /** + * columnCount + * Returns the number of columns in the result set + * + * @return integer Returns the number of columns in the result set represented + * by the PDOStatement object. If there is no result set, + * this method should return 0. + */ + function columnCount(); + + /** + * setFetchMode + * Set the fetch mode to use while iterating this statement. + * + * @param integer $fetchStyle + */ + function setFetchMode($fetchStyle, $arg2 = null, $arg3 = null); + + /** + * fetch + * + * @see Query::HYDRATE_* constants + * @param integer $fetchStyle Controls how the next row will be returned to the caller. + * This value must be one of the Query::HYDRATE_* constants, + * defaulting to Query::HYDRATE_BOTH + * + * @param integer $cursorOrientation For a PDOStatement object representing a scrollable cursor, + * this value determines which row will be returned to the caller. + * This value must be one of the Query::HYDRATE_ORI_* constants, defaulting to + * Query::HYDRATE_ORI_NEXT. To request a scrollable cursor for your + * PDOStatement object, + * you must set the PDO::ATTR_CURSOR attribute to Doctrine::CURSOR_SCROLL when you + * prepare the SQL statement with Doctrine_Adapter_Interface->prepare(). + * + * @param integer $cursorOffset For a PDOStatement object representing a scrollable cursor for which the + * $cursorOrientation parameter is set to Query::HYDRATE_ORI_ABS, this value specifies + * the absolute number of the row in the result set that shall be fetched. + * + * For a PDOStatement object representing a scrollable cursor for + * which the $cursorOrientation parameter is set to Query::HYDRATE_ORI_REL, this value + * specifies the row to fetch relative to the cursor position before + * PDOStatement->fetch() was called. + * + * @return mixed + */ + function fetch($fetchStyle = PDO::FETCH_BOTH); + + /** + * Returns an array containing all of the result set rows + * + * @param integer $fetchStyle Controls how the next row will be returned to the caller. + * This value must be one of the Query::HYDRATE_* constants, + * defaulting to Query::HYDRATE_BOTH + * + * @param integer $columnIndex Returns the indicated 0-indexed column when the value of $fetchStyle is + * Query::HYDRATE_COLUMN. Defaults to 0. + * + * @return array + */ + function fetchAll($fetchStyle = PDO::FETCH_BOTH); + + /** + * fetchColumn + * Returns a single column from the next row of a + * result set or FALSE if there are no more rows. + * + * @param integer $columnIndex 0-indexed number of the column you wish to retrieve from the row. If no + * value is supplied, PDOStatement->fetchColumn() + * fetches the first column. + * + * @return string returns a single column in the next row of a result set. + */ + function fetchColumn($columnIndex = 0); +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Statement.php new file mode 100644 index 0000000..d417833 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Statement.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use \PDO; + +/** + * Statement interface. + * Drivers must implement this interface. + * + * This resembles (a subset of) the PDOStatement interface. + * + * @author Konsta Vesterinen + * @author Roman Borschel + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + */ +interface Statement extends ResultStatement +{ + /** + * Binds a value to a corresponding named or positional + * placeholder in the SQL statement that was used to prepare the statement. + * + * @param mixed $param Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement + * using question mark placeholders, this will be the 1-indexed position of the parameter + * + * @param mixed $value The value to bind to the parameter. + * @param integer $type Explicit data type for the parameter using the PDO::PARAM_* constants. + * + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function bindValue($param, $value, $type = null); + + /** + * Binds a PHP variable to a corresponding named or question mark placeholder in the + * SQL statement that was use to prepare the statement. Unlike PDOStatement->bindValue(), + * the variable is bound as a reference and will only be evaluated at the time + * that PDOStatement->execute() is called. + * + * Most parameters are input parameters, that is, parameters that are + * used in a read-only fashion to build up the query. Some drivers support the invocation + * of stored procedures that return data as output parameters, and some also as input/output + * parameters that both send in data and are updated to receive it. + * + * @param mixed $param Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement + * using question mark placeholders, this will be the 1-indexed position of the parameter + * + * @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter. + * + * @param integer $type Explicit data type for the parameter using the PDO::PARAM_* constants. To return + * an INOUT parameter from a stored procedure, use the bitwise OR operator to set the + * PDO::PARAM_INPUT_OUTPUT bits for the data_type parameter. + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function bindParam($column, &$variable, $type = null); + + /** + * errorCode + * Fetch the SQLSTATE associated with the last operation on the statement handle + * + * @see Doctrine_Adapter_Interface::errorCode() + * @return string error code string + */ + function errorCode(); + + /** + * errorInfo + * Fetch extended error information associated with the last operation on the statement handle + * + * @see Doctrine_Adapter_Interface::errorInfo() + * @return array error info array + */ + function errorInfo(); + + /** + * Executes a prepared statement + * + * If the prepared statement included parameter markers, you must either: + * call PDOStatement->bindParam() to bind PHP variables to the parameter markers: + * bound variables pass their value as input and receive the output value, + * if any, of their associated parameter markers or pass an array of input-only + * parameter values + * + * + * @param array $params An array of values with as many elements as there are + * bound parameters in the SQL statement being executed. + * @return boolean Returns TRUE on success or FALSE on failure. + */ + function execute($params = null); + + /** + * rowCount + * rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement + * executed by the corresponding object. + * + * If the last SQL statement executed by the associated Statement object was a SELECT statement, + * some databases may return the number of rows returned by that statement. However, + * this behaviour is not guaranteed for all databases and should not be + * relied on for portable applications. + * + * @return integer Returns the number of rows. + */ + function rowCount(); +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php new file mode 100644 index 0000000..b670ee5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php @@ -0,0 +1,165 @@ +. + */ + +namespace Doctrine\DBAL; + +use Doctrine\Common\EventManager; + +/** + * Factory for creating Doctrine\DBAL\Connection instances. + * + * @author Roman Borschel + * @since 2.0 + */ +final class DriverManager +{ + /** + * List of supported drivers and their mappings to the driver classes. + * + * @var array + * @todo REMOVE. Users should directly supply class names instead. + */ + private static $_driverMap = array( + 'pdo_mysql' => 'Doctrine\DBAL\Driver\PDOMySql\Driver', + 'pdo_sqlite' => 'Doctrine\DBAL\Driver\PDOSqlite\Driver', + 'pdo_pgsql' => 'Doctrine\DBAL\Driver\PDOPgSql\Driver', + 'pdo_oci' => 'Doctrine\DBAL\Driver\PDOOracle\Driver', + 'oci8' => 'Doctrine\DBAL\Driver\OCI8\Driver', + 'ibm_db2' => 'Doctrine\DBAL\Driver\IBMDB2\DB2Driver', + 'pdo_ibm' => 'Doctrine\DBAL\Driver\PDOIbm\Driver', + 'pdo_sqlsrv' => 'Doctrine\DBAL\Driver\PDOSqlsrv\Driver', + 'mysqli' => 'Doctrine\DBAL\Driver\Mysqli\Driver', + ); + + /** Private constructor. This class cannot be instantiated. */ + private function __construct() { } + + /** + * Creates a connection object based on the specified parameters. + * This method returns a Doctrine\DBAL\Connection which wraps the underlying + * driver connection. + * + * $params must contain at least one of the following. + * + * Either 'driver' with one of the following values: + * pdo_mysql + * pdo_sqlite + * pdo_pgsql + * pdo_oracle + * pdo_sqlsrv + * + * OR 'driverClass' that contains the full class name (with namespace) of the + * driver class to instantiate. + * + * Other (optional) parameters: + * + * user (string): + * The username to use when connecting. + * + * password (string): + * The password to use when connecting. + * + * driverOptions (array): + * Any additional driver-specific options for the driver. These are just passed + * through to the driver. + * + * pdo: + * You can pass an existing PDO instance through this parameter. The PDO + * instance will be wrapped in a Doctrine\DBAL\Connection. + * + * wrapperClass: + * You may specify a custom wrapper class through the 'wrapperClass' + * parameter but this class MUST inherit from Doctrine\DBAL\Connection. + * + * driverClass: + * The driver class to use. + * + * @param array $params The parameters. + * @param Doctrine\DBAL\Configuration The configuration to use. + * @param Doctrine\Common\EventManager The event manager to use. + * @return Doctrine\DBAL\Connection + */ + public static function getConnection( + array $params, + Configuration $config = null, + EventManager $eventManager = null) + { + // create default config and event manager, if not set + if ( ! $config) { + $config = new Configuration(); + } + if ( ! $eventManager) { + $eventManager = new EventManager(); + } + + // check for existing pdo object + if (isset($params['pdo']) && ! $params['pdo'] instanceof \PDO) { + throw DBALException::invalidPdoInstance(); + } else if (isset($params['pdo'])) { + $params['pdo']->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $params['driver'] = 'pdo_' . $params['pdo']->getAttribute(\PDO::ATTR_DRIVER_NAME); + } else { + self::_checkParams($params); + } + if (isset($params['driverClass'])) { + $className = $params['driverClass']; + } else { + $className = self::$_driverMap[$params['driver']]; + } + + $driver = new $className(); + + $wrapperClass = 'Doctrine\DBAL\Connection'; + if (isset($params['wrapperClass'])) { + if (is_subclass_of($params['wrapperClass'], $wrapperClass)) { + $wrapperClass = $params['wrapperClass']; + } else { + throw DBALException::invalidWrapperClass($params['wrapperClass']); + } + } + + return new $wrapperClass($params, $driver, $config, $eventManager); + } + + /** + * Checks the list of parameters. + * + * @param array $params + */ + private static function _checkParams(array $params) + { + // check existance of mandatory parameters + + // driver + if ( ! isset($params['driver']) && ! isset($params['driverClass'])) { + throw DBALException::driverRequired(); + } + + // check validity of parameters + + // driver + if ( isset($params['driver']) && ! isset(self::$_driverMap[$params['driver']])) { + throw DBALException::unknownDriver($params['driver'], array_keys(self::$_driverMap)); + } + + if (isset($params['driverClass']) && ! in_array('Doctrine\DBAL\Driver', class_implements($params['driverClass'], true))) { + throw DBALException::invalidDriverClass($params['driverClass']); + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php new file mode 100644 index 0000000..ce80ecd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php @@ -0,0 +1,79 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\Common\EventArgs, + Doctrine\DBAL\Connection; + +/** + * Event Arguments used when a Driver connection is established inside Doctrine\DBAL\Connection. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class ConnectionEventArgs extends EventArgs +{ + /** + * @var Connection + */ + private $_connection = null; + + public function __construct(Connection $connection) + { + $this->_connection = $connection; + } + + /** + * @return Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return Doctrine\DBAL\Driver + */ + public function getDriver() + { + return $this->_connection->getDriver(); + } + + /** + * @return Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_connection->getDatabasePlatform(); + } + + /** + * @return Doctrine\DBAL\Schema\AbstractSchemaManager + */ + public function getSchemaManager() + { + return $this->_connection->getSchemaManager(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php new file mode 100644 index 0000000..a9718d5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php @@ -0,0 +1,74 @@ +. +*/ + +namespace Doctrine\DBAL\Event\Listeners; + +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventSubscriber; + +/** + * MySQL Session Init Event Subscriber which allows to set the Client Encoding of the Connection + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @deprecated Use "charset" option to PDO MySQL Connection instead. + */ +class MysqlSessionInit implements EventSubscriber +{ + /** + * @var string + */ + private $_charset; + + /** + * @var string + */ + private $_collation; + + /** + * Configure Charset and Collation options of MySQL Client for each Connection + * + * @param string $charset + * @param string $collation + */ + public function __construct($charset = 'utf8', $collation = false) + { + $this->_charset = $charset; + $this->_collation = $collation; + } + + /** + * @param ConnectionEventArgs $args + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + $collation = ($this->_collation) ? " COLLATE ".$this->_collation : ""; + $args->getConnection()->executeUpdate("SET NAMES ".$this->_charset . $collation); + } + + public function getSubscribedEvents() + { + return array(Events::postConnect); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php new file mode 100644 index 0000000..7f5caa3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php @@ -0,0 +1,79 @@ +. +*/ + +namespace Doctrine\DBAL\Event\Listeners; + +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventSubscriber; + +/** + * Should be used when Oracle Server default enviroment does not match the Doctrine requirements. + * + * The following enviroment variables are required for the Doctrine default date format: + * + * NLS_TIME_FORMAT="HH24:MI:SS" + * NLS_DATE_FORMAT="YYYY-MM-DD HH24:MI:SS" + * NLS_TIMESTAMP_FORMAT="YYYY-MM-DD HH24:MI:SS" + * NLS_TIMESTAMP_TZ_FORMAT="YYYY-MM-DD HH24:MI:SS TZH:TZM" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class OracleSessionInit implements EventSubscriber +{ + protected $_defaultSessionVars = array( + 'NLS_TIME_FORMAT' => "HH24:MI:SS", + 'NLS_DATE_FORMAT' => "YYYY-MM-DD HH24:MI:SS", + 'NLS_TIMESTAMP_FORMAT' => "YYYY-MM-DD HH24:MI:SS", + 'NLS_TIMESTAMP_TZ_FORMAT' => "YYYY-MM-DD HH24:MI:SS TZH:TZM", + ); + + /** + * @param array $oracleSessionVars + */ + public function __construct(array $oracleSessionVars = array()) + { + $this->_defaultSessionVars = array_merge($this->_defaultSessionVars, $oracleSessionVars); + } + + /** + * @param ConnectionEventArgs $args + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + if (count($this->_defaultSessionVars)) { + array_change_key_case($this->_defaultSessionVars, \CASE_UPPER); + $vars = array(); + foreach ($this->_defaultSessionVars AS $option => $value) { + $vars[] = $option." = '".$value."'"; + } + $sql = "ALTER SESSION SET ".implode(" ", $vars); + $args->getConnection()->executeUpdate($sql); + } + } + + public function getSubscribedEvents() + { + return array(Events::postConnect); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php new file mode 100644 index 0000000..670a61e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php @@ -0,0 +1,63 @@ +. +*/ + +namespace Doctrine\DBAL\Event\Listeners; + +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventSubscriber; + +/** + * Session init listener for executing a single SQL statement right after a connection is opened. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Benjamin Eberlei + */ +class SQLSessionInit implements EventSubscriber +{ + /** + * @var string + */ + protected $sql; + + /** + * @param string $sql + */ + public function __construct($sql) + { + $this->sql = $sql; + } + + /** + * @param ConnectionEventArgs $args + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + $conn = $args->getConnection(); + $conn->exec($this->sql); + } + + public function getSubscribedEvents() + { + return array(Events::postConnect); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php new file mode 100644 index 0000000..453f942 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php @@ -0,0 +1,114 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for adding table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableAddColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Column $column, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_column = $column; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * @return \Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php new file mode 100644 index 0000000..180fbee --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php @@ -0,0 +1,114 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\ColumnDiff, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for changing table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableChangeColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\ColumnDiff + */ + private $_columnDiff = null; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\ColumnDiff $columnDiff + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(ColumnDiff $columnDiff, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_columnDiff = $columnDiff; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\ColumnDiff + */ + public function getColumnDiff() + { + return $this->_columnDiff; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * @return \Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php new file mode 100644 index 0000000..0f9a0a5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php @@ -0,0 +1,99 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for creating tables are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * @return \Doctrine\DBAL\Event\SchemaAlterTableEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php new file mode 100644 index 0000000..b64bbc1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php @@ -0,0 +1,114 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for removing table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableRemoveColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Column $column, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_column = $column; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * @return \Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php new file mode 100644 index 0000000..4b9a88b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php @@ -0,0 +1,129 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for renaming table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableRenameColumnEventArgs extends SchemaEventArgs +{ + /** + * @var string + */ + private $_oldColumnName = null; + + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param string $oldColumnName + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct($oldColumnName, Column $column, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_oldColumnName = $oldColumnName; + $this->_column = $column; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return string + */ + public function getOldColumnName() + { + return $this->_oldColumnName; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * @return \Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php new file mode 100644 index 0000000..4cde46c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php @@ -0,0 +1,137 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Connection, + Doctrine\DBAL\Schema\Column; + +/** + * Event Arguments used when the portable column definition is generated inside Doctrine\DBAL\Schema\AbstractSchemaManager. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaColumnDefinitionEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * Raw column data as fetched from the database + * + * @var array + */ + private $_tableColumn = null; + + /** + * @var string + */ + private $_table = null; + + /** + * @var string + */ + private $_database = null; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection = null; + + /** + * @param array $tableColumn + * @param string $table + * @param string $database + * @param \Doctrine\DBAL\Connection $conn + */ + public function __construct(array $tableColumn, $table, $database, Connection $connection) + { + $this->_tableColumn = $tableColumn; + $this->_table = $table; + $this->_database = $database; + $this->_connection = $connection; + } + + /** + * Allows to clear the column which means the column will be excluded from + * tables column list. + * + * @param null|\Doctrine\DBAL\Schema\Column $column + * @return SchemaColumnDefinitionEventArgs + */ + public function setColumn(Column $column = null) + { + $this->_column = $column; + + return $this; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return array + */ + public function getTableColumn() + { + return $this->_tableColumn; + } + + /** + * @return string + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return string + */ + public function getDatabase() + { + return $this->_database; + } + + /** + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_connection->getDatabasePlatform(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php new file mode 100644 index 0000000..5e9a440 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php @@ -0,0 +1,114 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when SQL queries for creating table columns are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaCreateTableColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column = null; + + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $_table = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Column $column, Table $table, AbstractPlatform $platform) + { + $this->_column = $column; + $this->_table = $table; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\Table + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * @return \Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php new file mode 100644 index 0000000..138981c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php @@ -0,0 +1,128 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when SQL queries for creating tables are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaCreateTableEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $_table = null; + + /** + * @var array + */ + private $_columns = null; + + /** + * @var array + */ + private $_options = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param array $columns + * @param array $options + * @param Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Table $table, array $columns, array $options, AbstractPlatform $platform) + { + $this->_table = $table; + $this->_columns = $columns; + $this->_options = $options; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Table + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return array + */ + public function getColumns() + { + return $this->_columns; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->_options; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * @return \Doctrine\DBAL\Event\SchemaCreateTableEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php new file mode 100644 index 0000000..ebd2163 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php @@ -0,0 +1,98 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when the SQL query for dropping tables are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaDropTableEventArgs extends SchemaEventArgs +{ + /** + * @var string|\Doctrine\DBAL\Schema\Table + */ + private $_table = null; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @var string + */ + private $_sql = null; + + /** + * @param string|\Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct($table, AbstractPlatform $platform) + { + if (!$table instanceof Table && !is_string($table)) { + throw new \InvalidArgumentException('SchemaCreateTableEventArgs expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + $this->_table = $table; + $this->_platform = $platform; + } + + /** + * @return string|\Doctrine\DBAL\Schema\Table + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string $sql + * @return \Doctrine\DBAL\Event\SchemaDropTableEventArgs + */ + public function setSql($sql) + { + $this->_sql = $sql; + + return $this; + } + + /** + * @return string + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaEventArgs.php new file mode 100644 index 0000000..8181225 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaEventArgs.php @@ -0,0 +1,56 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\Common\EventArgs; + +/** + * Base class for schema related events. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaEventArgs extends EventArgs +{ + /** + * @var boolean + */ + private $_preventDefault = false; + + /** + * @return \Doctrine\DBAL\Event\SchemaEventArgs + */ + public function preventDefault() + { + $this->_preventDefault = true; + + return $this; + } + + /** + * @return boolean + */ + public function isDefaultPrevented() + { + return $this->_preventDefault; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php new file mode 100644 index 0000000..9eb95af --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php @@ -0,0 +1,122 @@ +. +*/ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Connection, + Doctrine\DBAL\Schema\Index; + +/** + * Event Arguments used when the portable index definition is generated inside Doctrine\DBAL\Schema\AbstractSchemaManager. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaIndexDefinitionEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Index + */ + private $_index = null; + + /** + * Raw index data as fetched from the database + * + * @var array + */ + private $_tableIndex = null; + + /** + * @var string + */ + private $_table = null; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection = null; + + /** + * @param array $tableIndex + * @param string $table + * @param \Doctrine\DBAL\Connection $conn + */ + public function __construct(array $tableIndex, $table, Connection $connection) + { + $this->_tableIndex = $tableIndex; + $this->_table = $table; + $this->_connection = $connection; + } + + /** + * Allows to clear the index which means the index will be excluded from + * tables index list. + * + * @param null|\Doctrine\DBAL\Schema\Index $index + * @return SchemaIndexDefinitionEventArgs + */ + public function setIndex(Index $index = null) + { + $this->_index = $index; + + return $this; + } + + /** + * @return \Doctrine\DBAL\Schema\Index + */ + public function getIndex() + { + return $this->_index; + } + + /** + * @return array + */ + public function getTableIndex() + { + return $this->_tableIndex; + } + + /** + * @return string + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_connection->getDatabasePlatform(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php new file mode 100644 index 0000000..25e31f0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Container for all DBAL events. + * + * This class cannot be instantiated. + * + * @author Roman Borschel + * @since 2.0 + */ +final class Events +{ + private function __construct() {} + + const postConnect = 'postConnect'; + + const onSchemaCreateTable = 'onSchemaCreateTable'; + const onSchemaCreateTableColumn = 'onSchemaCreateTableColumn'; + const onSchemaDropTable = 'onSchemaDropTable'; + const onSchemaAlterTable = 'onSchemaAlterTable'; + const onSchemaAlterTableAddColumn = 'onSchemaAlterTableAddColumn'; + const onSchemaAlterTableRemoveColumn = 'onSchemaAlterTableRemoveColumn'; + const onSchemaAlterTableChangeColumn = 'onSchemaAlterTableChangeColumn'; + const onSchemaAlterTableRenameColumn = 'onSchemaAlterTableRenameColumn'; + const onSchemaColumnDefinition = 'onSchemaColumnDefinition'; + const onSchemaIndexDefinition = 'onSchemaIndexDefinition'; +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/LockMode.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/LockMode.php new file mode 100644 index 0000000..dff96de --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/LockMode.php @@ -0,0 +1,42 @@ +. +*/ + +namespace Doctrine\DBAL; + +/** + * Contains all DBAL LockModes + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class LockMode +{ + const NONE = 0; + const OPTIMISTIC = 1; + const PESSIMISTIC_READ = 2; + const PESSIMISTIC_WRITE = 4; + + final private function __construct() { } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php new file mode 100644 index 0000000..7dca159 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Includes executed SQLs in a Debug Stack + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DebugStack implements SQLLogger +{ + /** @var array $queries Executed SQL queries. */ + public $queries = array(); + + /** @var boolean $enabled If Debug Stack is enabled (log queries) or not. */ + public $enabled = true; + + public $start = null; + + public $currentQuery = 0; + + /** + * {@inheritdoc} + */ + public function startQuery($sql, array $params = null, array $types = null) + { + if ($this->enabled) { + $this->start = microtime(true); + $this->queries[++$this->currentQuery] = array('sql' => $sql, 'params' => $params, 'types' => $types, 'executionMS' => 0); + } + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + $this->queries[$this->currentQuery]['executionMS'] = microtime(true) - $this->start; + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php new file mode 100644 index 0000000..2c3caf2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * A SQL logger that logs to the standard output using echo/var_dump. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EchoSQLLogger implements SQLLogger +{ + /** + * {@inheritdoc} + */ + public function startQuery($sql, array $params = null, array $types = null) + { + echo $sql . PHP_EOL; + + if ($params) { + var_dump($params); + } + + if ($types) { + var_dump($types); + } + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php new file mode 100644 index 0000000..0711c9b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Chains multiple SQLLogger + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Christophe Coevoet + */ +class LoggerChain implements SQLLogger +{ + private $loggers = array(); + + /** + * Adds a logger in the chain + * + * @param SQLLogger $logger + */ + public function addLogger(SQLLogger $logger) + { + $this->loggers[] = $logger; + } + + /** + * {@inheritdoc} + */ + public function startQuery($sql, array $params = null, array $types = null) + { + foreach ($this->loggers as $logger) { + $logger->startQuery($sql, $params, $types); + } + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + foreach ($this->loggers as $logger) { + $logger->stopQuery(); + } + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php new file mode 100644 index 0000000..6fccb6a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Interface for SQL loggers. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface SQLLogger +{ + /** + * Logs a SQL statement somewhere. + * + * @param string $sql The SQL to be executed. + * @param array $params The SQL parameters. + * @param array $types The SQL parameter types. + * @return void + */ + public function startQuery($sql, array $params = null, array $types = null); + + /** + * Mark the last started query as stopped. This can be used for timing of queries. + * + * @return void + */ + public function stopQuery(); +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php new file mode 100644 index 0000000..7f5024d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -0,0 +1,2645 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException, + Doctrine\DBAL\Connection, + Doctrine\DBAL\Types, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Index, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\TableDiff, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ColumnDiff, + Doctrine\DBAL\Types\Type, + Doctrine\DBAL\Events, + Doctrine\Common\EventManager, + Doctrine\DBAL\Event\SchemaCreateTableEventArgs, + Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs, + Doctrine\DBAL\Event\SchemaDropTableEventArgs, + Doctrine\DBAL\Event\SchemaAlterTableEventArgs, + Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs, + Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs, + Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs, + Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs; + +/** + * Base class for all DatabasePlatforms. The DatabasePlatforms are the central + * point of abstraction of platform-specific behaviors, features and SQL dialects. + * They are a passive source of information. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @todo Remove any unnecessary methods. + */ +abstract class AbstractPlatform +{ + /** + * @var int + */ + const CREATE_INDEXES = 1; + + /** + * @var int + */ + const CREATE_FOREIGNKEYS = 2; + + /** + * @var int + */ + const TRIM_UNSPECIFIED = 0; + + /** + * @var int + */ + const TRIM_LEADING = 1; + + /** + * @var int + */ + const TRIM_TRAILING = 2; + + /** + * @var int + */ + const TRIM_BOTH = 3; + + /** + * @var array + */ + protected $doctrineTypeMapping = null; + + /** + * Contains a list of all columns that should generate parseable column comments for type-detection + * in reverse engineering scenarios. + * + * @var array + */ + protected $doctrineTypeComments = null; + + /** + * @var Doctrine\Common\EventManager + */ + protected $_eventManager; + + /** + * Constructor. + */ + public function __construct() {} + + /** + * Sets the EventManager used by the Platform. + * + * @param \Doctrine\Common\EventManager + */ + public function setEventManager(EventManager $eventManager) + { + $this->_eventManager = $eventManager; + } + + /** + * Gets the EventManager used by the Platform. + * + * @return \Doctrine\Common\EventManager + */ + public function getEventManager() + { + return $this->_eventManager; + } + + /** + * Gets the SQL snippet that declares a boolean column. + * + * @param array $columnDef + * @return string + */ + abstract public function getBooleanTypeDeclarationSQL(array $columnDef); + + /** + * Gets the SQL snippet that declares a 4 byte integer column. + * + * @param array $columnDef + * @return string + */ + abstract public function getIntegerTypeDeclarationSQL(array $columnDef); + + /** + * Gets the SQL snippet that declares an 8 byte integer column. + * + * @param array $columnDef + * @return string + */ + abstract public function getBigIntTypeDeclarationSQL(array $columnDef); + + /** + * Gets the SQL snippet that declares a 2 byte integer column. + * + * @param array $columnDef + * @return string + */ + abstract public function getSmallIntTypeDeclarationSQL(array $columnDef); + + /** + * Gets the SQL snippet that declares common properties of an integer column. + * + * @param array $columnDef + * @return string + */ + abstract protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef); + + /** + * Lazy load Doctrine Type Mappings + * + * @return void + */ + abstract protected function initializeDoctrineTypeMappings(); + + /** + * Gets the SQL snippet used to declare a VARCHAR column type. + * + * @param array $field + */ + public function getVarcharTypeDeclarationSQL(array $field) + { + if ( !isset($field['length'])) { + $field['length'] = $this->getVarcharDefaultLength(); + } + + $fixed = (isset($field['fixed'])) ? $field['fixed'] : false; + + if ($field['length'] > $this->getVarcharMaxLength()) { + return $this->getClobTypeDeclarationSQL($field); + } else { + return $this->getVarcharTypeDeclarationSQLSnippet($field['length'], $fixed); + } + } + + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + throw DBALException::notSupported('VARCHARs not supported by Platform.'); + } + + /** + * Gets the SQL snippet used to declare a CLOB column type. + * + * @param array $field + */ + abstract public function getClobTypeDeclarationSQL(array $field); + + /** + * Gets the SQL Snippet used to declare a BLOB column type. + */ + abstract public function getBlobTypeDeclarationSQL(array $field); + + /** + * Gets the name of the platform. + * + * @return string + */ + abstract public function getName(); + + /** + * Register a doctrine type to be used in conjunction with a column type of this platform. + * + * @param string $dbType + * @param string $doctrineType + */ + public function registerDoctrineTypeMapping($dbType, $doctrineType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeDoctrineTypeMappings(); + } + + if (!Types\Type::hasType($doctrineType)) { + throw DBALException::typeNotFound($doctrineType); + } + + $dbType = strtolower($dbType); + $this->doctrineTypeMapping[$dbType] = $doctrineType; + } + + /** + * Get the Doctrine type that is mapped for the given database column type. + * + * @param string $dbType + * @return string + */ + public function getDoctrineTypeMapping($dbType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeDoctrineTypeMappings(); + } + + $dbType = strtolower($dbType); + if (isset($this->doctrineTypeMapping[$dbType])) { + return $this->doctrineTypeMapping[$dbType]; + } else { + throw new \Doctrine\DBAL\DBALException("Unknown database type ".$dbType." requested, " . get_class($this) . " may not support it."); + } + } + + /** + * Check if a database type is currently supported by this platform. + * + * @param string $dbType + * @return bool + */ + public function hasDoctrineTypeMappingFor($dbType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeDoctrineTypeMappings(); + } + + $dbType = strtolower($dbType); + return isset($this->doctrineTypeMapping[$dbType]); + } + + /** + * Initialize the Doctrine Type comments instance variable for in_array() checks. + * + * @return void + */ + protected function initializeCommentedDoctrineTypes() + { + $this->doctrineTypeComments = array(Type::TARRAY, Type::OBJECT); + } + + /** + * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type? + * + * @param Type $doctrineType + * @return bool + */ + public function isCommentedDoctrineType(Type $doctrineType) + { + if ($this->doctrineTypeComments === null) { + $this->initializeCommentedDoctrineTypes(); + } + + return in_array($doctrineType->getName(), $this->doctrineTypeComments); + } + + /** + * Mark this type as to be commented in ALTER TABLE and CREATE TABLE statements. + * + * @param Type $doctrineType + * @return void + */ + public function markDoctrineTypeCommented(Type $doctrineType) + { + if ($this->doctrineTypeComments === null) { + $this->initializeCommentedDoctrineTypes(); + } + $this->doctrineTypeComments[] = $doctrineType->getName(); + } + + /** + * Get the comment to append to a column comment that helps parsing this type in reverse engineering. + * + * @param Type $doctrineType + * @return string + */ + public function getDoctrineTypeComment(Type $doctrineType) + { + return '(DC2Type:' . $doctrineType->getName() . ')'; + } + + /** + * Return the comment of a passed column modified by potential doctrine type comment hints. + * + * @param Column $column + * @return string + */ + protected function getColumnComment(Column $column) + { + $comment = $column->getComment(); + if ($this->isCommentedDoctrineType($column->getType())) { + $comment .= $this->getDoctrineTypeComment($column->getType()); + } + return $comment; + } + + /** + * Gets the character used for identifier quoting. + * + * @return string + */ + public function getIdentifierQuoteCharacter() + { + return '"'; + } + + /** + * Gets the string portion that starts an SQL comment. + * + * @return string + */ + public function getSqlCommentStartString() + { + return "--"; + } + + /** + * Gets the string portion that ends an SQL comment. + * + * @return string + */ + public function getSqlCommentEndString() + { + return "\n"; + } + + /** + * Gets the maximum length of a varchar field. + * + * @return integer + */ + public function getVarcharMaxLength() + { + return 4000; + } + + /** + * Gets the default length of a varchar field. + * + * @return integer + */ + public function getVarcharDefaultLength() + { + return 255; + } + + /** + * Gets all SQL wildcard characters of the platform. + * + * @return array + */ + public function getWildcards() + { + return array('%', '_'); + } + + /** + * Returns the regular expression operator. + * + * @return string + */ + public function getRegexpExpression() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns global unique identifier + * + * @return string to get global unique identifier + */ + public function getGuidExpression() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the average value of a column + * + * @param string $column the column to use + * @return string generated sql including an AVG aggregate function + */ + public function getAvgExpression($column) + { + return 'AVG(' . $column . ')'; + } + + /** + * Returns the number of rows (without a NULL value) of a column + * + * If a '*' is used instead of a column the number of selected rows + * is returned. + * + * @param string|integer $column the column to use + * @return string generated sql including a COUNT aggregate function + */ + public function getCountExpression($column) + { + return 'COUNT(' . $column . ')'; + } + + /** + * Returns the highest value of a column + * + * @param string $column the column to use + * @return string generated sql including a MAX aggregate function + */ + public function getMaxExpression($column) + { + return 'MAX(' . $column . ')'; + } + + /** + * Returns the lowest value of a column + * + * @param string $column the column to use + * @return string + */ + public function getMinExpression($column) + { + return 'MIN(' . $column . ')'; + } + + /** + * Returns the total sum of a column + * + * @param string $column the column to use + * @return string + */ + public function getSumExpression($column) + { + return 'SUM(' . $column . ')'; + } + + // scalar functions + + /** + * Returns the md5 sum of a field. + * + * Note: Not SQL92, but common functionality + * + * @return string + */ + public function getMd5Expression($column) + { + return 'MD5(' . $column . ')'; + } + + /** + * Returns the length of a text field. + * + * @param string $expression1 + * @param string $expression2 + * @return string + */ + public function getLengthExpression($column) + { + return 'LENGTH(' . $column . ')'; + } + + /** + * Rounds a numeric field to the number of decimals specified. + * + * @param string $expression1 + * @param string $expression2 + * @return string + */ + public function getRoundExpression($column, $decimals = 0) + { + return 'ROUND(' . $column . ', ' . $decimals . ')'; + } + + /** + * Returns the remainder of the division operation + * $expression1 / $expression2. + * + * @param string $expression1 + * @param string $expression2 + * @return string + */ + public function getModExpression($expression1, $expression2) + { + return 'MOD(' . $expression1 . ', ' . $expression2 . ')'; + } + + /** + * Trim a string, leading/trailing/both and with a given char which defaults to space. + * + * @param string $str + * @param int $pos + * @param string $char has to be quoted already + * @return string + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + $posStr = ''; + $trimChar = ($char != false) ? $char . ' FROM ' : ''; + + if ($pos == self::TRIM_LEADING) { + $posStr = 'LEADING '.$trimChar; + } else if($pos == self::TRIM_TRAILING) { + $posStr = 'TRAILING '.$trimChar; + } else if($pos == self::TRIM_BOTH) { + $posStr = 'BOTH '.$trimChar; + } + + return 'TRIM(' . $posStr . $str . ')'; + } + + /** + * rtrim + * returns the string $str with proceeding space characters removed + * + * @param string $str literal string or column name + * @return string + */ + public function getRtrimExpression($str) + { + return 'RTRIM(' . $str . ')'; + } + + /** + * ltrim + * returns the string $str with leading space characters removed + * + * @param string $str literal string or column name + * @return string + */ + public function getLtrimExpression($str) + { + return 'LTRIM(' . $str . ')'; + } + + /** + * upper + * Returns the string $str with all characters changed to + * uppercase according to the current character set mapping. + * + * @param string $str literal string or column name + * @return string + */ + public function getUpperExpression($str) + { + return 'UPPER(' . $str . ')'; + } + + /** + * lower + * Returns the string $str with all characters changed to + * lowercase according to the current character set mapping. + * + * @param string $str literal string or column name + * @return string + */ + public function getLowerExpression($str) + { + return 'LOWER(' . $str . ')'; + } + + /** + * returns the position of the first occurrence of substring $substr in string $str + * + * @param string $substr literal string to find + * @param string $str literal string + * @param int $pos position to start at, beginning of string by default + * @return integer + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the current system date. + * + * @return string + */ + public function getNowExpression() + { + return 'NOW()'; + } + + /** + * return string to call a function to get a substring inside an SQL statement + * + * Note: Not SQL92, but common functionality. + * + * SQLite only supports the 2 parameter variant of this function + * + * @param string $value an sql string literal or column name/alias + * @param integer $from where to start the substring portion + * @param integer $len the substring portion length + * @return string + */ + public function getSubstringExpression($value, $from, $len = null) + { + if ($len === null) + return 'SUBSTRING(' . $value . ' FROM ' . $from . ')'; + else { + return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $len . ')'; + } + } + + /** + * Returns a series of strings concatinated + * + * concat() accepts an arbitrary number of parameters. Each parameter + * must contain an expression + * + * @param string $arg1, $arg2 ... $argN strings that will be concatinated. + * @return string + */ + public function getConcatExpression() + { + return join(' || ' , func_get_args()); + } + + /** + * Returns the SQL for a logical not. + * + * Example: + * + * $q = new Doctrine_Query(); + * $e = $q->expr; + * $q->select('*')->from('table') + * ->where($e->eq('id', $e->not('null')); + * + * + * @return string a logical expression + */ + public function getNotExpression($expression) + { + return 'NOT(' . $expression . ')'; + } + + /** + * Returns the SQL to check if a value is one in a set of + * given values. + * + * in() accepts an arbitrary number of parameters. The first parameter + * must always specify the value that should be matched against. Successive + * must contain a logical expression or an array with logical expressions. + * These expressions will be matched against the first parameter. + * + * @param string $column the value that should be matched against + * @param string|array(string) values that will be matched against $column + * @return string logical expression + */ + public function getInExpression($column, $values) + { + if ( ! is_array($values)) { + $values = array($values); + } + $values = $this->getIdentifiers($values); + + if (count($values) == 0) { + throw \InvalidArgumentException('Values must not be empty.'); + } + return $column . ' IN (' . implode(', ', $values) . ')'; + } + + /** + * Returns SQL that checks if a expression is null. + * + * @param string $expression the expression that should be compared to null + * @return string logical expression + */ + public function getIsNullExpression($expression) + { + return $expression . ' IS NULL'; + } + + /** + * Returns SQL that checks if a expression is not null. + * + * @param string $expression the expression that should be compared to null + * @return string logical expression + */ + public function getIsNotNullExpression($expression) + { + return $expression . ' IS NOT NULL'; + } + + /** + * Returns SQL that checks if an expression evaluates to a value between + * two values. + * + * The parameter $expression is checked if it is between $value1 and $value2. + * + * Note: There is a slight difference in the way BETWEEN works on some databases. + * http://www.w3schools.com/sql/sql_between.asp. If you want complete database + * independence you should avoid using between(). + * + * @param string $expression the value to compare to + * @param string $value1 the lower value to compare with + * @param string $value2 the higher value to compare with + * @return string logical expression + */ + public function getBetweenExpression($expression, $value1, $value2) + { + return $expression . ' BETWEEN ' .$value1 . ' AND ' . $value2; + } + + public function getAcosExpression($value) + { + return 'ACOS(' . $value . ')'; + } + + public function getSinExpression($value) + { + return 'SIN(' . $value . ')'; + } + + public function getPiExpression() + { + return 'PI()'; + } + + public function getCosExpression($value) + { + return 'COS(' . $value . ')'; + } + + /** + * Calculate the difference in days between the two passed dates. + * + * Computes diff = date1 - date2 + * + * @param string $date1 + * @param string $date2 + * @return string + */ + public function getDateDiffExpression($date1, $date2) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Add the number of given days to a date. + * + * @param string $date + * @param int $days + * @return string + */ + public function getDateAddDaysExpression($date, $days) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Substract the number of given days to a date. + * + * @param string $date + * @param int $days + * @return string + */ + public function getDateSubDaysExpression($date, $days) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Add the number of given months to a date. + * + * @param string $date + * @param int $months + * @return string + */ + public function getDateAddMonthExpression($date, $months) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Substract the number of given months to a date. + * + * @param string $date + * @param int $months + * @return string + */ + public function getDateSubMonthExpression($date, $months) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Gets SQL bit AND comparison expression + * + * @param string $value1 + * @param string $value2 + * @return string + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return '(' . $value1 . ' & ' . $value2 . ')'; + } + + /** + * Gets SQL bit OR comparison expression + * + * @param string $value1 + * @param string $value2 + * @return string + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return '(' . $value1 . ' | ' . $value2 . ')'; + } + + public function getForUpdateSQL() + { + return 'FOR UPDATE'; + } + + /** + * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification. + * + * @param string $fromClause + * @param int $lockMode + * @return string + */ + public function appendLockHint($fromClause, $lockMode) + { + return $fromClause; + } + + /** + * Get the sql snippet to append to any SELECT statement which locks rows in shared read lock. + * + * This defaults to the ASNI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database + * vendors allow to lighten this constraint up to be a real read lock. + * + * @return string + */ + public function getReadLockSQL() + { + return $this->getForUpdateSQL(); + } + + /** + * Get the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows. + * + * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ASNI SQL standard. + * + * @return string + */ + public function getWriteLockSQL() + { + return $this->getForUpdateSQL(); + } + + public function getDropDatabaseSQL($database) + { + return 'DROP DATABASE ' . $database; + } + + /** + * Drop a Table + * + * @throws \InvalidArgumentException + * @param Table|string $table + * @return string + */ + public function getDropTableSQL($table) + { + $tableArg = $table; + + if ($table instanceof \Doctrine\DBAL\Schema\Table) { + $table = $table->getQuotedName($this); + } else if(!is_string($table)) { + throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) { + $eventArgs = new SchemaDropTableEventArgs($tableArg, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs); + + if ($eventArgs->isDefaultPrevented()) { + return $eventArgs->getSql(); + } + } + + return 'DROP TABLE ' . $table; + } + + /** + * Get SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction. + * + * @param Table|string $table + * @return string + */ + public function getDropTemporaryTableSQL($table) + { + return $this->getDropTableSQL($table); + } + + /** + * Drop index from a table + * + * @param Index|string $name + * @param string|Table $table + * @return string + */ + public function getDropIndexSQL($index, $table=null) + { + if($index instanceof \Doctrine\DBAL\Schema\Index) { + $index = $index->getQuotedName($this); + } else if(!is_string($index)) { + throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + return 'DROP INDEX ' . $index; + } + + /** + * Get drop constraint sql + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param string|Table $table + * @return string + */ + public function getDropConstraintSQL($constraint, $table) + { + if ($constraint instanceof \Doctrine\DBAL\Schema\Constraint) { + $constraint = $constraint->getQuotedName($this); + } + + if ($table instanceof \Doctrine\DBAL\Schema\Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint; + } + + /** + * @param ForeignKeyConstraint|string $foreignKey + * @param Table|string $table + * @return string + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if ($foreignKey instanceof \Doctrine\DBAL\Schema\ForeignKeyConstraint) { + $foreignKey = $foreignKey->getQuotedName($this); + } + + if ($table instanceof \Doctrine\DBAL\Schema\Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey; + } + + /** + * Gets the SQL statement(s) to create a table with the specified name, columns and constraints + * on this platform. + * + * @param string $table The name of the table. + * @param int $createFlags + * @return array The sequence of SQL statements. + */ + public function getCreateTableSQL(Table $table, $createFlags=self::CREATE_INDEXES) + { + if ( ! is_int($createFlags)) { + throw new \InvalidArgumentException("Second argument of AbstractPlatform::getCreateTableSQL() has to be integer."); + } + + if (count($table->getColumns()) == 0) { + throw DBALException::noColumnsSpecifiedForTable($table->getName()); + } + + $tableName = $table->getQuotedName($this); + $options = $table->getOptions(); + $options['uniqueConstraints'] = array(); + $options['indexes'] = array(); + $options['primary'] = array(); + + if (($createFlags&self::CREATE_INDEXES) > 0) { + foreach ($table->getIndexes() AS $index) { + /* @var $index Index */ + if ($index->isPrimary()) { + $options['primary'] = $index->getColumns(); + } else { + $options['indexes'][$index->getName()] = $index; + } + } + } + + $columnSql = array(); + $columns = array(); + foreach ($table->getColumns() AS $column) { + /* @var \Doctrine\DBAL\Schema\Column $column */ + + if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) { + $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + if ($eventArgs->isDefaultPrevented()) { + continue; + } + } + + $columnData = array(); + $columnData['name'] = $column->getQuotedName($this); + $columnData['type'] = $column->getType(); + $columnData['length'] = $column->getLength(); + $columnData['notnull'] = $column->getNotNull(); + $columnData['fixed'] = $column->getFixed(); + $columnData['unique'] = false; // TODO: what do we do about this? + $columnData['version'] = ($column->hasPlatformOption("version"))?$column->getPlatformOption('version'):false; + if(strtolower($columnData['type']) == "string" && $columnData['length'] === null) { + $columnData['length'] = 255; + } + $columnData['unsigned'] = $column->getUnsigned(); + $columnData['precision'] = $column->getPrecision(); + $columnData['scale'] = $column->getScale(); + $columnData['default'] = $column->getDefault(); + $columnData['columnDefinition'] = $column->getColumnDefinition(); + $columnData['autoincrement'] = $column->getAutoincrement(); + $columnData['comment'] = $this->getColumnComment($column); + + if(in_array($column->getName(), $options['primary'])) { + $columnData['primary'] = true; + } + + $columns[$columnData['name']] = $columnData; + } + + if (($createFlags&self::CREATE_FOREIGNKEYS) > 0) { + $options['foreignKeys'] = array(); + foreach ($table->getForeignKeys() AS $fkConstraint) { + $options['foreignKeys'][] = $fkConstraint; + } + } + + if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) { + $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs); + + if ($eventArgs->isDefaultPrevented()) { + return array_merge($eventArgs->getSql(), $columnSql); + } + } + + $sql = $this->_getCreateTableSQL($tableName, $columns, $options); + if ($this->supportsCommentOnStatement()) { + foreach ($table->getColumns() AS $column) { + if ($this->getColumnComment($column)) { + $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getName(), $this->getColumnComment($column)); + } + } + } + + return array_merge($sql, $columnSql); + } + + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + return "COMMENT ON COLUMN " . $tableName . "." . $columnName . " IS '" . $comment . "'"; + } + + /** + * @param string $tableName + * @param array $columns + * @param array $options + * @return array + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $columnListSql = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $name => $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if (isset($options['primary']) && ! empty($options['primary'])) { + $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')'; + } + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach($options['indexes'] as $index => $definition) { + $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition); + } + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql; + + $check = $this->getCheckDeclarationSQL($columns); + if ( ! empty($check)) { + $query .= ', ' . $check; + } + $query .= ')'; + + $sql[] = $query; + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] AS $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE TEMPORARY TABLE"; + } + + /** + * Gets the SQL to create a sequence on this platform. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * @throws DBALException + */ + public function getCreateSequenceSQL(\Doctrine\DBAL\Schema\Sequence $sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Gets the SQL statement to change a sequence on this platform. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * @return string + */ + public function getAlterSequenceSQL(\Doctrine\DBAL\Schema\Sequence $sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Gets the SQL to create a constraint on a table on this platform. + * + * @param Constraint $constraint + * @param string|Table $table + * @return string + */ + public function getCreateConstraintSQL(\Doctrine\DBAL\Schema\Constraint $constraint, $table) + { + if ($table instanceof \Doctrine\DBAL\Schema\Table) { + $table = $table->getQuotedName($this); + } + + $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this); + + $columns = array(); + foreach ($constraint->getColumns() as $column) { + $columns[] = $column; + } + $columnList = '('. implode(', ', $columns) . ')'; + + $referencesClause = ''; + if ($constraint instanceof \Doctrine\DBAL\Schema\Index) { + if($constraint->isPrimary()) { + $query .= ' PRIMARY KEY'; + } elseif ($constraint->isUnique()) { + $query .= ' UNIQUE'; + } else { + throw new \InvalidArgumentException( + 'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().' + ); + } + } else if ($constraint instanceof \Doctrine\DBAL\Schema\ForeignKeyConstraint) { + $query .= ' FOREIGN KEY'; + + $foreignColumns = array(); + foreach ($constraint->getForeignColumns() AS $column) { + $foreignColumns[] = $column; + } + + $referencesClause = ' REFERENCES '.$constraint->getForeignTableName(). ' ('.implode(', ', $foreignColumns).')'; + } + $query .= ' '.$columnList.$referencesClause; + + return $query; + } + + /** + * Gets the SQL to create an index on a table on this platform. + * + * @param Index $index + * @param string|Table $table name of the table on which the index is to be created + * @return string + */ + public function getCreateIndexSQL(Index $index, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + $name = $index->getQuotedName($this); + $columns = $index->getColumns(); + + if (count($columns) == 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + if ($index->isPrimary()) { + return $this->getCreatePrimaryKeySQL($index, $table); + } else { + $type = ''; + if ($index->isUnique()) { + $type = 'UNIQUE '; + } + + $query = 'CREATE ' . $type . 'INDEX ' . $name . ' ON ' . $table; + $query .= ' (' . $this->getIndexFieldDeclarationListSQL($columns) . ')'; + } + + return $query; + } + + /** + * Get SQL to create an unnamed primary key constraint. + * + * @param Index $index + * @param string|Table $table + * @return string + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index->getColumns()) . ')'; + } + + /** + * Quotes a string so that it can be safely used as a table or column name, + * even if it is a reserved word of the platform. This also detects identifier + * chains seperated by dot and quotes them independently. + * + * NOTE: Just because you CAN use quoted identifiers doesn't mean + * you SHOULD use them. In general, they end up causing way more + * problems than they solve. + * + * @param string $str identifier name to be quoted + * @return string quoted identifier string + */ + public function quoteIdentifier($str) + { + if (strpos($str, ".") !== false) { + $parts = array_map(array($this, "quoteIdentifier"), explode(".", $str)); + return implode(".", $parts); + } + + return $this->quoteSingleIdentifier($str); + } + + /** + * Quote a single identifier (no dot chain seperation) + * + * @param string $str + * @return string + */ + public function quoteSingleIdentifier($str) + { + $c = $this->getIdentifierQuoteCharacter(); + + return $c . str_replace($c, $c.$c, $str) . $c; + } + + /** + * Create a new foreign key + * + * @param ForeignKeyConstraint $foreignKey ForeignKey instance + * @param string|Table $table name of the table on which the foreign key is to be created + * @return string + */ + public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) + { + if ($table instanceof \Doctrine\DBAL\Schema\Table) { + $table = $table->getQuotedName($this); + } + + $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey); + + return $query; + } + + /** + * Gets the sql statements for altering an existing table. + * + * The method returns an array of sql statements, since some platforms need several statements. + * + * @param TableDiff $diff + * @return array + */ + public function getAlterTableSQL(TableDiff $diff) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param Column $column + * @param TableDiff $diff + * @param array $columnSql + */ + protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if (!$this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param Column $column + * @param TableDiff $diff + * @param array $columnSql + */ + protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if (!$this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param ColumnDiff $columnDiff + * @param TableDiff $diff + * @param array $columnSql + */ + protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if (!$this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param string $oldColumnName + * @param Column $column + * @param TableDiff $diff + * @param array $columnSql + */ + protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if (!$this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + /** + * @param TableDiff $diff + * @param array $columnSql + */ + protected function onSchemaAlterTable(TableDiff $diff, &$sql) + { + if (null === $this->_eventManager) { + return false; + } + + if (!$this->_eventManager->hasListeners(Events::onSchemaAlterTable)) { + return false; + } + + $eventArgs = new SchemaAlterTableEventArgs($diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs); + + $sql = array_merge($sql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $tableName = $diff->name; + + $sql = array(); + if ($this->supportsForeignKeyConstraints()) { + foreach ($diff->removedForeignKeys AS $foreignKey) { + $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + } + foreach ($diff->changedForeignKeys AS $foreignKey) { + $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + } + } + + foreach ($diff->removedIndexes AS $index) { + $sql[] = $this->getDropIndexSQL($index, $tableName); + } + foreach ($diff->changedIndexes AS $index) { + $sql[] = $this->getDropIndexSQL($index, $tableName); + } + + return $sql; + } + + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + if ($diff->newName !== false) { + $tableName = $diff->newName; + } else { + $tableName = $diff->name; + } + + $sql = array(); + if ($this->supportsForeignKeyConstraints()) { + foreach ($diff->addedForeignKeys AS $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + } + foreach ($diff->changedForeignKeys AS $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + } + } + + foreach ($diff->addedIndexes AS $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + foreach ($diff->changedIndexes AS $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + + return $sql; + } + + /** + * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions. + * + * @param TableDiff $diff + * @return array + */ + protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff) + { + return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff)); + } + + /** + * Get declaration of a number of fields in bulk + * + * @param array $fields a multidimensional associative array. + * The first dimension determines the field name, while the second + * dimension is keyed with the name of the properties + * of the field being declared as array indexes. Currently, the types + * of supported field properties are as follows: + * + * length + * Integer value that determines the maximum length of the text + * field. If this argument is missing the field should be + * declared to have the longest length allowed by the DBMS. + * + * default + * Text value to be used as default for this field. + * + * notnull + * Boolean flag that indicates whether this field is constrained + * to not be set to null. + * charset + * Text value with the default CHARACTER SET for this field. + * collation + * Text value with the default COLLATION for this field. + * unique + * unique constraint + * + * @return string + */ + public function getColumnDeclarationListSQL(array $fields) + { + $queryFields = array(); + foreach ($fields as $fieldName => $field) { + $query = $this->getColumnDeclarationSQL($fieldName, $field); + $queryFields[] = $query; + } + return implode(', ', $queryFields); + } + + /** + * Obtain DBMS specific SQL code portion needed to declare a generic type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param array $field associative array with the name of the properties + * of the field being declared as array indexes. Currently, the types + * of supported field properties are as follows: + * + * length + * Integer value that determines the maximum length of the text + * field. If this argument is missing the field should be + * declared to have the longest length allowed by the DBMS. + * + * default + * Text value to be used as default for this field. + * + * notnull + * Boolean flag that indicates whether this field is constrained + * to not be set to null. + * charset + * Text value with the default CHARACTER SET for this field. + * collation + * Text value with the default COLLATION for this field. + * unique + * unique constraint + * check + * column check constraint + * columnDefinition + * a string that defines the complete column + * + * @return string DBMS specific SQL code portion that should be used to declare the column. + */ + public function getColumnDeclarationSQL($name, array $field) + { + if (isset($field['columnDefinition'])) { + $columnDef = $this->getCustomTypeDeclarationSQL($field); + } else { + $default = $this->getDefaultValueDeclarationSQL($field); + + $charset = (isset($field['charset']) && $field['charset']) ? + ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : ''; + + $collation = (isset($field['collation']) && $field['collation']) ? + ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : ''; + + $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : ''; + + $unique = (isset($field['unique']) && $field['unique']) ? + ' ' . $this->getUniqueFieldDeclarationSQL() : ''; + + $check = (isset($field['check']) && $field['check']) ? + ' ' . $field['check'] : ''; + + $typeDecl = $field['type']->getSqlDeclaration($field, $this); + $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation; + } + + if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment']) { + $columnDef .= " COMMENT '" . $field['comment'] . "'"; + } + + return $name . ' ' . $columnDef; + } + + /** + * Gets the SQL snippet that declares a floating point column of arbitrary precision. + * + * @param array $columnDef + * @return string + */ + public function getDecimalTypeDeclarationSQL(array $columnDef) + { + $columnDef['precision'] = ( ! isset($columnDef['precision']) || empty($columnDef['precision'])) + ? 10 : $columnDef['precision']; + $columnDef['scale'] = ( ! isset($columnDef['scale']) || empty($columnDef['scale'])) + ? 0 : $columnDef['scale']; + + return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set a default value + * declaration to be used in statements like CREATE TABLE. + * + * @param array $field field definition array + * @return string DBMS specific SQL code portion needed to set a default value + */ + public function getDefaultValueDeclarationSQL($field) + { + $default = empty($field['notnull']) ? ' DEFAULT NULL' : ''; + + if (isset($field['default'])) { + $default = " DEFAULT '".$field['default']."'"; + if (isset($field['type'])) { + if (in_array((string)$field['type'], array("Integer", "BigInteger", "SmallInteger"))) { + $default = " DEFAULT ".$field['default']; + } else if ((string)$field['type'] == 'DateTime' && $field['default'] == $this->getCurrentTimestampSQL()) { + $default = " DEFAULT ".$this->getCurrentTimestampSQL(); + + } else if ((string) $field['type'] == 'Boolean') { + $default = " DEFAULT '" . $this->convertBooleans($field['default']) . "'"; + } + } + } + return $default; + } + + /** + * Obtain DBMS specific SQL code portion needed to set a CHECK constraint + * declaration to be used in statements like CREATE TABLE. + * + * @param array $definition check definition + * @return string DBMS specific SQL code portion needed to set a CHECK constraint + */ + public function getCheckDeclarationSQL(array $definition) + { + $constraints = array(); + foreach ($definition as $field => $def) { + if (is_string($def)) { + $constraints[] = 'CHECK (' . $def . ')'; + } else { + if (isset($def['min'])) { + $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')'; + } + + if (isset($def['max'])) { + $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')'; + } + } + } + + return implode(', ', $constraints); + } + + /** + * Obtain DBMS specific SQL code portion needed to set a unique + * constraint declaration to be used in statements like CREATE TABLE. + * + * @param string $name name of the unique constraint + * @param Index $index index definition + * @return string DBMS specific SQL code portion needed + * to set a constraint + */ + public function getUniqueConstraintDeclarationSQL($name, Index $index) + { + if (count($index->getColumns()) == 0) { + throw \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + return 'CONSTRAINT ' . $name . ' UNIQUE (' + . $this->getIndexFieldDeclarationListSQL($index->getColumns()) + . ')'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param string $name name of the index + * @param Index $index index definition + * @return string DBMS specific SQL code portion needed to set an index + */ + public function getIndexDeclarationSQL($name, Index $index) + { + $type = ''; + + if($index->isUnique()) { + $type = 'UNIQUE '; + } + + if (count($index->getColumns()) == 0) { + throw \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + return $type . 'INDEX ' . $name . ' (' + . $this->getIndexFieldDeclarationListSQL($index->getColumns()) + . ')'; + } + + /** + * getCustomTypeDeclarationSql + * Obtail SQL code portion needed to create a custom column, + * e.g. when a field has the "columnDefinition" keyword. + * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate. + * + * @return string + */ + public function getCustomTypeDeclarationSQL(array $columnDef) + { + return $columnDef['columnDefinition']; + } + + /** + * getIndexFieldDeclarationList + * Obtain DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @return string + */ + public function getIndexFieldDeclarationListSQL(array $fields) + { + $ret = array(); + foreach ($fields as $field => $definition) { + if (is_array($definition)) { + $ret[] = $field; + } else { + $ret[] = $definition; + } + } + return implode(', ', $ret); + } + + /** + * A method to return the required SQL string that fits between CREATE ... TABLE + * to create the table as a temporary table. + * + * Should be overridden in driver classes to return the correct string for the + * specific database type. + * + * The default is to return the string "TEMPORARY" - this will result in a + * SQL error for any database that does not support temporary tables, or that + * requires a different SQL command from "CREATE TEMPORARY TABLE". + * + * @return string The string required to be placed between "CREATE" and "TABLE" + * to generate a temporary table, if possible. + */ + public function getTemporaryTableSQL() + { + return 'TEMPORARY'; + } + + /** + * Some vendors require temporary table names to be qualified specially. + * + * @param string $tableName + * @return string + */ + public function getTemporaryTableName($tableName) + { + return $tableName; + } + + /** + * Get sql query to show a list of database. + * + * @return string + */ + public function getShowDatabasesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param array $definition an associative array with the following structure: + * name optional constraint name + * + * local the local field(s) + * + * foreign the foreign reference field(s) + * + * foreignTable the name of the foreign table + * + * onDelete referential delete action + * + * onUpdate referential update action + * + * deferred deferred constraint checking + * + * The onDelete and onUpdate keys accept the following values: + * + * CASCADE: Delete or update the row from the parent table and automatically delete or + * update the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported. + * Between two tables, you should not define several ON UPDATE CASCADE clauses that act on the same column + * in the parent table or in the child table. + * + * SET NULL: Delete or update the row from the parent table and set the foreign key column or columns in the + * child table to NULL. This is valid only if the foreign key columns do not have the NOT NULL qualifier + * specified. Both ON DELETE SET NULL and ON UPDATE SET NULL clauses are supported. + * + * NO ACTION: In standard SQL, NO ACTION means no action in the sense that an attempt to delete or update a primary + * key value is not allowed to proceed if there is a related foreign key value in the referenced table. + * + * RESTRICT: Rejects the delete or update operation for the parent table. NO ACTION and RESTRICT are the same as + * omitting the ON DELETE or ON UPDATE clause. + * + * SET DEFAULT + * + * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration. + */ + public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + $sql = $this->getForeignKeyBaseDeclarationSQL($foreignKey); + $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey); + + return $sql; + } + + /** + * Return the FOREIGN KEY query section dealing with non-standard options + * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... + * + * @param ForeignKeyConstraint $foreignKey foreign key definition + * @return string + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = ''; + if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) { + $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate')); + } + if ($foreignKey->hasOption('onDelete')) { + $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); + } + return $query; + } + + /** + * returns given referential action in uppercase if valid, otherwise throws + * an exception + * + * @throws Doctrine_Exception_Exception if unknown referential action given + * @param string $action foreign key referential action + * @param string foreign key referential action in uppercase + */ + public function getForeignKeyReferentialActionSQL($action) + { + $upper = strtoupper($action); + switch ($upper) { + case 'CASCADE': + case 'SET NULL': + case 'NO ACTION': + case 'RESTRICT': + case 'SET DEFAULT': + return $upper; + break; + default: + throw new \InvalidArgumentException('Invalid foreign key action: ' . $upper); + } + } + + /** + * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param ForeignKeyConstraint $foreignKey + * @return string + */ + public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + $sql = ''; + if (strlen($foreignKey->getName())) { + $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' '; + } + $sql .= 'FOREIGN KEY ('; + + if (count($foreignKey->getLocalColumns()) == 0) { + throw new \InvalidArgumentException("Incomplete definition. 'local' required."); + } + if (count($foreignKey->getForeignColumns()) == 0) { + throw new \InvalidArgumentException("Incomplete definition. 'foreign' required."); + } + if (strlen($foreignKey->getForeignTableName()) == 0) { + throw new \InvalidArgumentException("Incomplete definition. 'foreignTable' required."); + } + + $sql .= implode(', ', $foreignKey->getLocalColumns()) + . ') REFERENCES ' + . $foreignKey->getForeignTableName() . ' (' + . implode(', ', $foreignKey->getForeignColumns()) . ')'; + + return $sql; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the UNIQUE constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint + * of a field declaration. + */ + public function getUniqueFieldDeclarationSQL() + { + return 'UNIQUE'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param string $charset name of the charset + * @return string DBMS specific SQL code portion needed to set the CHARACTER SET + * of a field declaration. + */ + public function getColumnCharsetDeclarationSQL($charset) + { + return ''; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param string $collation name of the collation + * @return string DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration. + */ + public function getColumnCollationDeclarationSQL($collation) + { + return ''; + } + + /** + * Whether the platform prefers sequences for ID generation. + * Subclasses should override this method to return TRUE if they prefer sequences. + * + * @return boolean + */ + public function prefersSequences() + { + return false; + } + + /** + * Whether the platform prefers identity columns (eg. autoincrement) for ID generation. + * Subclasses should override this method to return TRUE if they prefer identity columns. + * + * @return boolean + */ + public function prefersIdentityColumns() + { + return false; + } + + /** + * Some platforms need the boolean values to be converted. + * + * The default conversion in this implementation converts to integers (false => 0, true => 1). + * + * @param mixed $item + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $k => $value) { + if (is_bool($value)) { + $item[$k] = (int) $value; + } + } + } else if (is_bool($item)) { + $item = (int) $item; + } + return $item; + } + + /** + * Gets the SQL statement specific for the platform to set the charset. + * + * This function is MySQL specific and required by + * {@see \Doctrine\DBAL\Connection::setCharset($charset)} + * + * @param string $charset + * @return string + */ + public function getSetCharsetSQL($charset) + { + return "SET NAMES '".$charset."'"; + } + + /** + * Gets the SQL specific for the platform to get the current date. + * + * @return string + */ + public function getCurrentDateSQL() + { + return 'CURRENT_DATE'; + } + + /** + * Gets the SQL specific for the platform to get the current time. + * + * @return string + */ + public function getCurrentTimeSQL() + { + return 'CURRENT_TIME'; + } + + /** + * Gets the SQL specific for the platform to get the current timestamp + * + * @return string + */ + public function getCurrentTimestampSQL() + { + return 'CURRENT_TIMESTAMP'; + } + + /** + * Get sql for transaction isolation level Connection constant + * + * @param integer $level + */ + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case Connection::TRANSACTION_READ_UNCOMMITTED: + return 'READ UNCOMMITTED'; + case Connection::TRANSACTION_READ_COMMITTED: + return 'READ COMMITTED'; + case Connection::TRANSACTION_REPEATABLE_READ: + return 'REPEATABLE READ'; + case Connection::TRANSACTION_SERIALIZABLE: + return 'SERIALIZABLE'; + default: + throw new \InvalidArgumentException('Invalid isolation level:' . $level); + } + } + + public function getListDatabasesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListSequencesSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableConstraintsSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableColumnsSQL($table, $database = null) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTablesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListUsersSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Get the SQL to list all views of a database or user. + * + * @param string $database + * @return string + */ + public function getListViewsSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Get the list of indexes for the current database. + * + * The current database parameter is optional but will always be passed + * when using the SchemaManager API and is the database the given table is in. + * + * Attention: Some platforms only support currentDatabase when they + * are connected with that database. Cross-database information schema + * requests may be impossible. + * + * @param string $table + * @param string $currentDatabase + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableForeignKeysSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getCreateViewSQL($name, $sql) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getDropViewSQL($name) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getDropSequenceSQL($sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getSequenceNextValSQL($sequenceName) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getCreateDatabaseSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Get sql to set the transaction isolation level + * + * @param integer $level + */ + public function getSetTransactionIsolationSQL($level) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtain DBMS specific SQL to be used to create datetime fields in + * statements like CREATE TABLE + * + * @param array $fieldDeclaration + * @return string + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtain DBMS specific SQL to be used to create datetime with timezone offset fields. + * + * @param array $fieldDeclaration + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration); + } + + + /** + * Obtain DBMS specific SQL to be used to create date fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * @return string + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtain DBMS specific SQL to be used to create time fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * @return string + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getFloatDeclarationSQL(array $fieldDeclaration) + { + return 'DOUBLE PRECISION'; + } + + /** + * Gets the default transaction isolation level of the platform. + * + * @return integer The default isolation level. + * @see Doctrine\DBAL\Connection\TRANSACTION_* constants. + */ + public function getDefaultTransactionIsolationLevel() + { + return Connection::TRANSACTION_READ_COMMITTED; + } + + /* supports*() methods */ + + /** + * Whether the platform supports sequences. + * + * @return boolean + */ + public function supportsSequences() + { + return false; + } + + /** + * Whether the platform supports identity columns. + * Identity columns are columns that recieve an auto-generated value from the + * database on insert of a row. + * + * @return boolean + */ + public function supportsIdentityColumns() + { + return false; + } + + /** + * Whether the platform supports indexes. + * + * @return boolean + */ + public function supportsIndexes() + { + return true; + } + + public function supportsAlterTable() + { + return true; + } + + /** + * Whether the platform supports transactions. + * + * @return boolean + */ + public function supportsTransactions() + { + return true; + } + + /** + * Whether the platform supports savepoints. + * + * @return boolean + */ + public function supportsSavepoints() + { + return true; + } + + /** + * Whether the platform supports releasing savepoints. + * + * @return boolean + */ + public function supportsReleaseSavepoints() + { + return $this->supportsSavepoints(); + } + + /** + * Whether the platform supports primary key constraints. + * + * @return boolean + */ + public function supportsPrimaryConstraints() + { + return true; + } + + /** + * Does the platform supports foreign key constraints? + * + * @return boolean + */ + public function supportsForeignKeyConstraints() + { + return true; + } + + /** + * Does this platform supports onUpdate in foreign key constraints? + * + * @return bool + */ + public function supportsForeignKeyOnUpdate() + { + return ($this->supportsForeignKeyConstraints() && true); + } + + /** + * Whether the platform supports database schemas. + * + * @return boolean + */ + public function supportsSchemas() + { + return false; + } + + /** + * Can this platform emulate schemas? + * + * Platforms that either support or emulate schemas don't automatically + * filter a schema for the namespaced elements in {@link + * AbstractManager#createSchema}. + * + * @return bool + */ + public function canEmulateSchemas() + { + return false; + } + + /** + * Some databases don't allow to create and drop databases at all or only with certain tools. + * + * @return bool + */ + public function supportsCreateDropDatabase() + { + return true; + } + + /** + * Whether the platform supports getting the affected rows of a recent + * update/delete type query. + * + * @return boolean + */ + public function supportsGettingAffectedRows() + { + return true; + } + + /** + * Does this plaform support to add inline column comments as postfix. + * + * @return bool + */ + public function supportsInlineColumnComments() + { + return false; + } + + /** + * Does this platform support the propriortary synatx "COMMENT ON asset" + * + * @return bool + */ + public function supportsCommentOnStatement() + { + return false; + } + + public function getIdentityColumnNullInsertSQL() + { + return ""; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored datetime value of this platform. + * + * @return string The format string. + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s'; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored datetime with timezone value of this platform. + * + * @return string The format string. + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:s'; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored date value of this platform. + * + * @return string The format string. + */ + public function getDateFormatString() + { + return 'Y-m-d'; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored time value of this platform. + * + * @return string The format string. + */ + public function getTimeFormatString() + { + return 'H:i:s'; + } + + /** + * Modify limit query + * + * @param string $query + * @param int $limit + * @param int $offset + * @return string + */ + final public function modifyLimitQuery($query, $limit, $offset = null) + { + if ( $limit !== null) { + $limit = (int)$limit; + } + + if ( $offset !== null) { + $offset = (int)$offset; + + if ($offset < 0) { + throw new DBALException("LIMIT argument offset=$offset is not valid"); + } + if ( $offset > 0 && ! $this->supportsLimitOffset()) { + throw new DBALException(sprintf("Platform %s does not support offset values in limit queries.", $this->getName())); + } + } + + return $this->doModifyLimitQuery($query, $limit, $offset); + } + + /** + * @param string $query + * @param int $limit + * @param int $offset + * @return string + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if ( $limit !== null) { + $query .= ' LIMIT ' . $limit; + } + + if ( $offset !== null) { + $query .= ' OFFSET ' . $offset; + } + + return $query; + } + + /** + * Does the database platform support offsets in modify limit clauses? + * + * @return bool + */ + public function supportsLimitOffset() + { + return true; + } + + /** + * Gets the character casing of a column in an SQL result set of this platform. + * + * @param string $column The column name for which to get the correct character casing. + * @return string The column name in the character casing used in SQL result sets. + */ + public function getSQLResultCasing($column) + { + return $column; + } + + /** + * Makes any fixes to a name of a schema element (table, sequence, ...) that are required + * by restrictions of the platform, like a maximum length. + * + * @param string $schemaName + * @return string + */ + public function fixSchemaElementName($schemaElementName) + { + return $schemaElementName; + } + + /** + * Maximum length of any given databse identifier, like tables or column names. + * + * @return int + */ + public function getMaxIdentifierLength() + { + return 63; + } + + /** + * Get the insert sql for an empty insert statement + * + * @param string $tableName + * @param string $identifierColumnName + * @return string $sql + */ + public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName) + { + return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)'; + } + + /** + * Generate a Truncate Table SQL statement for a given table. + * + * Cascade is not supported on many platforms but would optionally cascade the truncate by + * following the foreign keys. + * + * @param string $tableName + * @param bool $cascade + * @return string + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE '.$tableName; + } + + /** + * This is for test reasons, many vendors have special requirements for dummy statements. + * + * @return string + */ + public function getDummySelectSQL() + { + return 'SELECT 1'; + } + + /** + * Generate SQL to create a new savepoint + * + * @param string $savepoint + * @return string + */ + public function createSavePoint($savepoint) + { + return 'SAVEPOINT ' . $savepoint; + } + + /** + * Generate SQL to release a savepoint + * + * @param string $savepoint + * @return string + */ + public function releaseSavePoint($savepoint) + { + return 'RELEASE SAVEPOINT ' . $savepoint; + } + + /** + * Generate SQL to rollback a savepoint + * + * @param string $savepoint + * @return string + */ + public function rollbackSavePoint($savepoint) + { + return 'ROLLBACK TO SAVEPOINT ' . $savepoint; + } + + /** + * Return the keyword list instance of this platform. + * + * Throws exception if no keyword list is specified. + * + * @throws DBALException + * @return KeywordList + */ + final public function getReservedKeywordsList() + { + $class = $this->getReservedKeywordsClass(); + $keywords = new $class; + if (!$keywords instanceof \Doctrine\DBAL\Platforms\Keywords\KeywordList) { + throw DBALException::notSupported(__METHOD__); + } + return $keywords; + } + + /** + * The class name of the reserved keywords list. + * + * @return string + */ + protected function getReservedKeywordsClass() + { + throw DBALException::notSupported(__METHOD__); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php new file mode 100644 index 0000000..198be83 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php @@ -0,0 +1,587 @@ +. +*/ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\TableDiff; + +class DB2Platform extends AbstractPlatform +{ + /** + * Gets the SQL Snippet used to declare a BLOB column type. + */ + public function getBlobTypeDeclarationSQL(array $field) + { + throw DBALException::notSupported(__METHOD__); + } + + public function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'smallint' => 'smallint', + 'bigint' => 'bigint', + 'integer' => 'integer', + 'time' => 'time', + 'date' => 'date', + 'varchar' => 'string', + 'character' => 'string', + 'clob' => 'text', + 'decimal' => 'decimal', + 'double' => 'float', + 'real' => 'float', + 'timestamp' => 'datetime', + ); + } + + /** + * Gets the SQL snippet used to declare a VARCHAR column type. + * + * @param array $field + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * Gets the SQL snippet used to declare a CLOB column type. + * + * @param array $field + */ + public function getClobTypeDeclarationSQL(array $field) + { + // todo clob(n) with $field['length']; + return 'CLOB(1M)'; + } + + /** + * Gets the name of the platform. + * + * @return string + */ + public function getName() + { + return 'db2'; + } + + + /** + * Gets the SQL snippet that declares a boolean column. + * + * @param array $columnDef + * @return string + */ + public function getBooleanTypeDeclarationSQL(array $columnDef) + { + return 'SMALLINT'; + } + + /** + * Gets the SQL snippet that declares a 4 byte integer column. + * + * @param array $columnDef + * @return string + */ + public function getIntegerTypeDeclarationSQL(array $columnDef) + { + return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * Gets the SQL snippet that declares an 8 byte integer column. + * + * @param array $columnDef + * @return string + */ + public function getBigIntTypeDeclarationSQL(array $columnDef) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * Gets the SQL snippet that declares a 2 byte integer column. + * + * @param array $columnDef + * @return string + */ + public function getSmallIntTypeDeclarationSQL(array $columnDef) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * Gets the SQL snippet that declares common properties of an integer column. + * + * @param array $columnDef + * @return string + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' GENERATED BY DEFAULT AS IDENTITY'; + } + return $autoinc; + } + + /** + * Obtain DBMS specific SQL to be used to create datetime fields in + * statements like CREATE TABLE + * + * @param array $fieldDeclaration + * @return string + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return "TIMESTAMP(0) WITH DEFAULT"; + } + + return 'TIMESTAMP(0)'; + } + + /** + * Obtain DBMS specific SQL to be used to create date fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * @return string + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * Obtain DBMS specific SQL to be used to create time fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * @return string + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + public function getListDatabasesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListSequencesSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getListTableConstraintsSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * This code fragment is originally from the Zend_Db_Adapter_Db2 class. + * + * @license New BSD License + * @param string $table + * @return string + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT DISTINCT c.tabschema, c.tabname, c.colname, c.colno, + c.typename, c.default, c.nulls, c.length, c.scale, + c.identity, tc.type AS tabconsttype, k.colseq + FROM syscat.columns c + LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc + ON (k.tabschema = tc.tabschema + AND k.tabname = tc.tabname + AND tc.type = 'P')) + ON (c.tabschema = k.tabschema + AND c.tabname = k.tabname + AND c.colname = k.colname) + WHERE UPPER(c.tabname) = UPPER('" . $table . "') ORDER BY c.colno"; + } + + public function getListTablesSQL() + { + return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'"; + } + + public function getListUsersSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Get the SQL to list all views of a database or user. + * + * @param string $database + * @return string + */ + public function getListViewsSQL($database) + { + return "SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS"; + } + + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "SELECT NAME, COLNAMES, UNIQUERULE FROM SYSIBM.SYSINDEXES WHERE TBNAME = UPPER('" . $table . "')"; + } + + public function getListTableForeignKeysSQL($table) + { + return "SELECT TBNAME, RELNAME, REFTBNAME, DELETERULE, UPDATERULE, FKCOLNAMES, PKCOLNAMES ". + "FROM SYSIBM.SYSRELS WHERE TBNAME = UPPER('".$table."')"; + } + + public function getCreateViewSQL($name, $sql) + { + return "CREATE VIEW ".$name." AS ".$sql; + } + + public function getDropViewSQL($name) + { + return "DROP VIEW ".$name; + } + + public function getDropSequenceSQL($sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getSequenceNextValSQL($sequenceName) + { + throw DBALException::notSupported(__METHOD__); + } + + public function getCreateDatabaseSQL($database) + { + return "CREATE DATABASE ".$database; + } + + public function getDropDatabaseSQL($database) + { + return "DROP DATABASE ".$database.";"; + } + + public function supportsCreateDropDatabase() + { + return false; + } + + /** + * Whether the platform supports releasing savepoints. + * + * @return boolean + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * Gets the SQL specific for the platform to get the current date. + * + * @return string + */ + public function getCurrentDateSQL() + { + return 'VALUES CURRENT DATE'; + } + + /** + * Gets the SQL specific for the platform to get the current time. + * + * @return string + */ + public function getCurrentTimeSQL() + { + return 'VALUES CURRENT TIME'; + } + + /** + * Gets the SQL specific for the platform to get the current timestamp + * + * @return string + */ + + public function getCurrentTimestampSQL() + { + return "VALUES CURRENT TIMESTAMP"; + } + + /** + * Obtain DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param string $name name of the index + * @param Index $index index definition + * @return string DBMS specific SQL code portion needed to set an index + */ + public function getIndexDeclarationSQL($name, Index $index) + { + return $this->getUniqueConstraintDeclarationSQL($name, $index); + } + + /** + * @param string $tableName + * @param array $columns + * @param array $options + * @return array + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $indexes = array(); + if (isset($options['indexes'])) { + $indexes = $options['indexes']; + } + $options['indexes'] = array(); + + $sqls = parent::_getCreateTableSQL($tableName, $columns, $options); + + foreach ($indexes as $index => $definition) { + $sqls[] = $this->getCreateIndexSQL($definition, $tableName); + } + return $sqls; + } + + /** + * Gets the SQL to alter an existing table. + * + * @param TableDiff $diff + * @return array + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $columnSql = array(); + + $queryParts = array(); + foreach ($diff->addedColumns AS $fieldName => $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + foreach ($diff->removedColumns AS $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); + } + + foreach ($diff->changedColumns AS $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + /* @var $columnDiff Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + $queryParts[] = 'ALTER ' . ($columnDiff->oldColumnName) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + foreach ($diff->renamedColumns AS $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'RENAME ' . $oldColumnName . ' TO ' . $column->getQuotedName($this); + } + + $tableSql = array(); + + if (!$this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . implode(" ", $queryParts); + } + + $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff)); + + if ($diff->newName !== false) { + $sql[] = 'RENAME TABLE TO ' . $diff->newName; + } + } + + return array_merge($sql, $tableSql, $columnSql); + } + + public function getDefaultValueDeclarationSQL($field) + { + if (isset($field['notnull']) && $field['notnull'] && !isset($field['default'])) { + if (in_array((string)$field['type'], array("Integer", "BigInteger", "SmallInteger"))) { + $field['default'] = 0; + } else if((string)$field['type'] == "DateTime") { + $field['default'] = "00-00-00 00:00:00"; + } else if ((string)$field['type'] == "Date") { + $field['default'] = "00-00-00"; + } else if((string)$field['type'] == "Time") { + $field['default'] = "00:00:00"; + } else { + $field['default'] = ''; + } + } + + unset($field['default']); // @todo this needs fixing + if (isset($field['version']) && $field['version']) { + if ((string)$field['type'] != "DateTime") { + $field['default'] = "1"; + } + } + + return parent::getDefaultValueDeclarationSQL($field); + } + + /** + * Get the insert sql for an empty insert statement + * + * @param string $tableName + * @param string $identifierColumnName + * @return string $sql + */ + public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName) + { + return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)'; + } + + public function getCreateTemporaryTableSnippetSQL() + { + return "DECLARE GLOBAL TEMPORARY TABLE"; + } + + /** + * DB2 automatically moves temporary tables into the SESSION. schema. + * + * @param string $tableName + * @return string + */ + public function getTemporaryTableName($tableName) + { + return "SESSION." . $tableName; + } + + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + if ($limit === null && $offset === null) { + return $query; + } + + $limit = (int)$limit; + $offset = (int)(($offset)?:0); + + // Todo OVER() needs ORDER BY data! + $sql = 'SELECT db22.* FROM (SELECT ROW_NUMBER() OVER() AS DC_ROWNUM, db21.* '. + 'FROM (' . $query . ') db21) db22 WHERE db22.DC_ROWNUM BETWEEN ' . ($offset+1) .' AND ' . ($offset+$limit); + return $sql; + } + + /** + * returns the position of the first occurrence of substring $substr in string $str + * + * @param string $substr literal string to find + * @param string $str literal string + * @param int $pos position to start at, beginning of string by default + * @return integer + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } else { + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + } + + /** + * return string to call a function to get a substring inside an SQL statement + * + * Note: Not SQL92, but common functionality. + * + * SQLite only supports the 2 parameter variant of this function + * + * @param string $value an sql string literal or column name/alias + * @param integer $from where to start the substring portion + * @param integer $len the substring portion length + * @return string + */ + public function getSubstringExpression($value, $from, $len = null) + { + if ($len === null) + return 'SUBSTR(' . $value . ', ' . $from . ')'; + else { + return 'SUBSTR(' . $value . ', ' . $from . ', ' . $len . ')'; + } + } + + public function supportsIdentityColumns() + { + return true; + } + + public function prefersIdentityColumns() + { + return true; + } + + /** + * Gets the character casing of a column in an SQL result set of this platform. + * + * DB2 returns all column names in SQL result sets in uppercase. + * + * @param string $column The column name for which to get the correct character casing. + * @return string The column name in the character casing used in SQL result sets. + */ + public function getSQLResultCasing($column) + { + return strtoupper($column); + } + + public function getForUpdateSQL() + { + return ' WITH RR USE AND KEEP UPDATE LOCKS'; + } + + public function getDummySelectSQL() + { + return 'SELECT 1 FROM sysibm.sysdummy1'; + } + + /** + * DB2 supports savepoints, but they work semantically different than on other vendor platforms. + * + * TODO: We have to investigate how to get DB2 up and running with savepoints. + * + * @return bool + */ + public function supportsSavepoints() + { + return false; + } + + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\DB2Keywords'; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php new file mode 100644 index 0000000..4b77cac --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php @@ -0,0 +1,438 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * DB2 Keywords + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class DB2Keywords extends KeywordList +{ + public function getName() + { + return 'DB2'; + } + + protected function getKeywords() + { + return array( + 'ACTIVATE', + 'ADD', + 'AFTER', + 'ALIAS', + 'ALL', + 'ALLOCATE', + 'DOCUMENT', + 'DOUBLE', + 'DROP', + 'DSSIZE', + 'DYNAMIC', + 'EACH', + 'LOCK', + 'LOCKMAX', + 'LOCKSIZE', + 'LONG', + 'LOOP', + 'MAINTAINED', + 'ROUND_CEILING', + 'ROUND_DOWN', + 'ROUND_FLOOR', + 'ROUND_HALF_DOWN', + 'ROUND_HALF_EVEN', + 'ROUND_HALF_UP', + 'ALLOW', + 'ALTER', + 'AND', + 'ANY', + 'AS', + 'ASENSITIVE', + 'ASSOCIATE', + 'ASUTIME', + 'AT', + 'ATTRIBUTES', + 'AUDIT', + 'AUTHORIZATION', + 'AUX', + 'AUXILIARY', + 'BEFORE', + 'BEGIN', + 'BETWEEN', + 'BINARY', + 'BUFFERPOOL', + 'BY', + 'CACHE', + 'CALL', + 'CALLED', + 'CAPTURE', + 'CARDINALITY', + 'CASCADED', + 'CASE', + 'CAST', + 'CCSID', + 'CHAR', + 'CHARACTER', + 'CHECK', + 'CLONE', + 'CLOSE', + 'CLUSTER', + 'COLLECTION', + 'COLLID', + 'COLUMN', + 'COMMENT', + 'COMMIT', + 'CONCAT', + 'CONDITION', + 'CONNECT', + 'CONNECTION', + 'CONSTRAINT', + 'CONTAINS', + 'CONTINUE', + 'COUNT', + 'COUNT_BIG', + 'CREATE', + 'CROSS', + 'CURRENT', + 'CURRENT_DATE', + 'CURRENT_LC_CTYPE', + 'CURRENT_PATH', + 'CURRENT_SCHEMA', + 'CURRENT_SERVER', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_TIMEZONE', + 'CURRENT_USER', + 'CURSOR', + 'CYCLE', + 'DATA', + 'DATABASE', + 'DATAPARTITIONNAME', + 'DATAPARTITIONNUM', + 'EDITPROC', + 'ELSE', + 'ELSEIF', + 'ENABLE', + 'ENCODING', + 'ENCRYPTION', + 'END', + 'END-EXEC', + 'ENDING', + 'ERASE', + 'ESCAPE', + 'EVERY', + 'EXCEPT', + 'EXCEPTION', + 'EXCLUDING', + 'EXCLUSIVE', + 'EXECUTE', + 'EXISTS', + 'EXIT', + 'EXPLAIN', + 'EXTERNAL', + 'EXTRACT', + 'FENCED', + 'FETCH', + 'FIELDPROC', + 'FILE', + 'FINAL', + 'FOR', + 'FOREIGN', + 'FREE', + 'FROM', + 'FULL', + 'FUNCTION', + 'GENERAL', + 'GENERATED', + 'GET', + 'GLOBAL', + 'GO', + 'GOTO', + 'GRANT', + 'GRAPHIC', + 'GROUP', + 'HANDLER', + 'HASH', + 'HASHED_VALUE', + 'HAVING', + 'HINT', + 'HOLD', + 'HOUR', + 'HOURS', + 'IDENTITY', + 'IF', + 'IMMEDIATE', + 'IN', + 'INCLUDING', + 'INCLUSIVE', + 'INCREMENT', + 'INDEX', + 'INDICATOR', + 'INF', + 'INFINITY', + 'INHERIT', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INTEGRITY', + 'MATERIALIZED', + 'MAXVALUE', + 'MICROSECOND', + 'MICROSECONDS', + 'MINUTE', + 'MINUTES', + 'MINVALUE', + 'MODE', + 'MODIFIES', + 'MONTH', + 'MONTHS', + 'NAN', + 'NEW', + 'NEW_TABLE', + 'NEXTVAL', + 'NO', + 'NOCACHE', + 'NOCYCLE', + 'NODENAME', + 'NODENUMBER', + 'NOMAXVALUE', + 'NOMINVALUE', + 'NONE', + 'NOORDER', + 'NORMALIZED', + 'NOT', + 'NULL', + 'NULLS', + 'NUMPARTS', + 'OBID', + 'OF', + 'OLD', + 'OLD_TABLE', + 'ON', + 'OPEN', + 'OPTIMIZATION', + 'OPTIMIZE', + 'OPTION', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OVER', + 'OVERRIDING', + 'PACKAGE', + 'PADDED', + 'PAGESIZE', + 'PARAMETER', + 'PART', + 'PARTITION', + 'PARTITIONED', + 'PARTITIONING', + 'PARTITIONS', + 'PASSWORD', + 'PATH', + 'PIECESIZE', + 'PLAN', + 'POSITION', + 'PRECISION', + 'PREPARE', + 'PREVVAL', + 'PRIMARY', + 'PRIQTY', + 'PRIVILEGES', + 'PROCEDURE', + 'PROGRAM', + 'PSID', + 'ROUND_UP', + 'ROUTINE', + 'ROW', + 'ROW_NUMBER', + 'ROWNUMBER', + 'ROWS', + 'ROWSET', + 'RRN', + 'RUN', + 'SAVEPOINT', + 'SCHEMA', + 'SCRATCHPAD', + 'SCROLL', + 'SEARCH', + 'SECOND', + 'SECONDS', + 'SECQTY', + 'SECURITY', + 'SELECT', + 'SENSITIVE', + 'SEQUENCE', + 'SESSION', + 'SESSION_USER', + 'SET', + 'SIGNAL', + 'SIMPLE', + 'SNAN', + 'SOME', + 'SOURCE', + 'SPECIFIC', + 'SQL', + 'SQLID', + 'STACKED', + 'STANDARD', + 'START', + 'STARTING', + 'STATEMENT', + 'STATIC', + 'STATMENT', + 'STAY', + 'STOGROUP', + 'STORES', + 'STYLE', + 'SUBSTRING', + 'SUMMARY', + 'SYNONYM', + 'SYSFUN', + 'SYSIBM', + 'SYSPROC', + 'SYSTEM', + 'SYSTEM_USER', + 'TABLE', + 'TABLESPACE', + 'THEN', + 'TIME', + 'TIMESTAMP', + 'TO', + 'TRANSACTION', + 'TRIGGER', + 'TRIM', + 'TRUNCATE', + 'TYPE', + 'UNDO', + 'UNION', + 'UNIQUE', + 'UNTIL', + 'UPDATE', + 'DATE', + 'DAY', + 'DAYS', + 'DB2GENERAL', + 'DB2GENRL', + 'DB2SQL', + 'DBINFO', + 'DBPARTITIONNAME', + 'DBPARTITIONNUM', + 'DEALLOCATE', + 'DECLARE', + 'DEFAULT', + 'DEFAULTS', + 'DEFINITION', + 'DELETE', + 'DENSE_RANK', + 'DENSERANK', + 'DESCRIBE', + 'DESCRIPTOR', + 'DETERMINISTIC', + 'DIAGNOSTICS', + 'DISABLE', + 'DISALLOW', + 'DISCONNECT', + 'DISTINCT', + 'DO', + 'INTERSECT', + 'PUBLIC', + 'USAGE', + 'INTO', + 'QUERY', + 'USER', + 'IS', + 'QUERYNO', + 'USING', + 'ISOBID', + 'RANGE', + 'VALIDPROC', + 'ISOLATION', + 'RANK', + 'VALUE', + 'ITERATE', + 'READ', + 'VALUES', + 'JAR', + 'READS', + 'VARIABLE', + 'JAVA', + 'RECOVERY', + 'VARIANT', + 'JOIN', + 'REFERENCES', + 'VCAT', + 'KEEP', + 'REFERENCING', + 'VERSION', + 'KEY', + 'REFRESH', + 'VIEW', + 'LABEL', + 'RELEASE', + 'VOLATILE', + 'LANGUAGE', + 'RENAME', + 'VOLUMES', + 'LATERAL', + 'REPEAT', + 'WHEN', + 'LC_CTYPE', + 'RESET', + 'WHENEVER', + 'LEAVE', + 'RESIGNAL', + 'WHERE', + 'LEFT', + 'RESTART', + 'WHILE', + 'LIKE', + 'RESTRICT', + 'WITH', + 'LINKTYPE', + 'RESULT', + 'WITHOUT', + 'LOCAL', + 'RESULT_SET_LOCATOR WLM', + 'LOCALDATE', + 'RETURN', + 'WRITE', + 'LOCALE', + 'RETURNS', + 'XMLELEMENT', + 'LOCALTIME', + 'REVOKE', + 'XMLEXISTS', + 'LOCALTIMESTAMP RIGHT', + 'XMLNAMESPACES', + 'LOCATOR', + 'ROLE', + 'YEAR', + 'LOCATORS', + 'ROLLBACK', + 'YEARS', + ); + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php new file mode 100644 index 0000000..dccd717 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php @@ -0,0 +1,63 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Abstract interface for a SQL reserved keyword dictionary. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +abstract class KeywordList +{ + private $keywords = null; + + /** + * Check if the given word is a keyword of this dialect/vendor platform. + * + * @param string $word + * @return bool + */ + public function isKeyword($word) + { + if ($this->keywords === null) { + $this->initializeKeywords(); + } + + return isset($this->keywords[strtoupper($word)]); + } + + protected function initializeKeywords() + { + $this->keywords = array_flip(array_map('strtoupper', $this->getKeywords())); + } + + abstract protected function getKeywords(); + + /** + * Name of this keyword list. + * + * @return string + */ + abstract public function getName(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php new file mode 100644 index 0000000..f59ae81 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php @@ -0,0 +1,243 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * MsSQL Keywordlist + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + */ +class MsSQLKeywords extends KeywordList +{ + public function getName() + { + return 'MsSQL'; + } + + protected function getKeywords() + { + return array( + 'ADD', + 'CURRENT_TIMESTAMP', + 'GROUP', + 'OPENQUERY', + 'SERIALIZABLE', + 'ALL', + 'CURRENT_USER', + 'HAVING', + 'OPENROWSET', + 'SESSION_USER', + 'ALTER', + 'CURSOR', + 'HOLDLOCK', + 'OPTION', + 'SET', + 'AND', + 'DATABASE', + 'IDENTITY', + 'OR', + 'SETUSER', + 'ANY', + 'DBCC', + 'IDENTITYCOL', + 'ORDER', + 'SHUTDOWN', + 'AS', + 'DEALLOCATE', + 'IDENTITY_INSERT', + 'OUTER', + 'SOME', + 'ASC', + 'DECLARE', + 'IF', + 'OVER', + 'STATISTICS', + 'AUTHORIZATION', + 'DEFAULT', + 'IN', + 'PERCENT', + 'SUM', + 'AVG', + 'DELETE', + 'INDEX', + 'PERM', + 'SYSTEM_USER', + 'BACKUP', + 'DENY', + 'INNER', + 'PERMANENT', + 'TABLE', + 'BEGIN', + 'DESC', + 'INSERT', + 'PIPE', + 'TAPE', + 'BETWEEN', + 'DISK', + 'INTERSECT', + 'PLAN', + 'TEMP', + 'BREAK', + 'DISTINCT', + 'INTO', + 'PRECISION', + 'TEMPORARY', + 'BROWSE', + 'DISTRIBUTED', + 'IS', + 'PREPARE', + 'TEXTSIZE', + 'BULK', + 'DOUBLE', + 'ISOLATION', + 'PRIMARY', + 'THEN', + 'BY', + 'DROP', + 'JOIN', + 'PRINT', + 'TO', + 'CASCADE', + 'DUMMY', + 'KEY', + 'PRIVILEGES', + 'TOP', + 'CASE', + 'DUMP', + 'KILL', + 'PROC', + 'TRAN', + 'CHECK', + 'ELSE', + 'LEFT', + 'PROCEDURE', + 'TRANSACTION', + 'CHECKPOINT', + 'END', + 'LEVEL', + 'PROCESSEXIT', + 'TRIGGER', + 'CLOSE', + 'ERRLVL', + 'LIKE', + 'PUBLIC', + 'TRUNCATE', + 'CLUSTERED', + 'ERROREXIT', + 'LINENO', + 'RAISERROR', + 'TSEQUAL', + 'COALESCE', + 'ESCAPE', + 'LOAD', + 'READ', + 'UNCOMMITTED', + 'COLUMN', + 'EXCEPT', + 'MAX', + 'READTEXT', + 'UNION', + 'COMMIT', + 'EXEC', + 'MIN', + 'RECONFIGURE', + 'UNIQUE', + 'COMMITTED', + 'EXECUTE', + 'MIRROREXIT', + 'REFERENCES', + 'UPDATE', + 'COMPUTE', + 'EXISTS', + 'NATIONAL', + 'REPEATABLE', + 'UPDATETEXT', + 'CONFIRM', + 'EXIT', + 'NOCHECK', + 'REPLICATION', + 'USE', + 'CONSTRAINT', + 'FETCH', + 'NONCLUSTERED', + 'RESTORE', + 'USER', + 'CONTAINS', + 'FILE', + 'NOT', + 'RESTRICT', + 'VALUES', + 'CONTAINSTABLE', + 'FILLFACTOR', + 'NULL', + 'RETURN', + 'VARYING', + 'CONTINUE', + 'FLOPPY', + 'NULLIF', + 'REVOKE', + 'VIEW', + 'CONTROLROW', + 'FOR', + 'OF', + 'RIGHT', + 'WAITFOR', + 'CONVERT', + 'FOREIGN', + 'OFF', + 'ROLLBACK', + 'WHEN', + 'COUNT', + 'FREETEXT', + 'OFFSETS', + 'ROWCOUNT', + 'WHERE', + 'CREATE', + 'FREETEXTTABLE', + 'ON', + 'ROWGUIDCOL', + 'WHILE', + 'CROSS', + 'FROM', + 'ONCE', + 'RULE', + 'WITH', + 'CURRENT', + 'FULL', + 'ONLY', + 'SAVE', + 'WORK', + 'CURRENT_DATE', + 'GOTO', + 'OPEN', + 'SCHEMA', + 'WRITETEXT', + 'CURRENT_TIME', + 'GRANT', + 'OPENDATASOURCE', + 'SELECT', + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php new file mode 100644 index 0000000..1d58749 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php @@ -0,0 +1,268 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * MySQL Keywordlist + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + */ +class MySQLKeywords extends KeywordList +{ + public function getName() + { + return 'MySQL'; + } + + protected function getKeywords() + { + return array( + 'ADD', + 'ALL', + 'ALTER', + 'ANALYZE', + 'AND', + 'AS', + 'ASC', + 'ASENSITIVE', + 'BEFORE', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BLOB', + 'BOTH', + 'BY', + 'CALL', + 'CASCADE', + 'CASE', + 'CHANGE', + 'CHAR', + 'CHARACTER', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONDITION', + 'CONNECTION', + 'CONSTRAINT', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATABASE', + 'DATABASES', + 'DAY_HOUR', + 'DAY_MICROSECOND', + 'DAY_MINUTE', + 'DAY_SECOND', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELAYED', + 'DELETE', + 'DESC', + 'DESCRIBE', + 'DETERMINISTIC', + 'DISTINCT', + 'DISTINCTROW', + 'DIV', + 'DOUBLE', + 'DROP', + 'DUAL', + 'EACH', + 'ELSE', + 'ELSEIF', + 'ENCLOSED', + 'ESCAPED', + 'EXISTS', + 'EXIT', + 'EXPLAIN', + 'FALSE', + 'FETCH', + 'FLOAT', + 'FLOAT4', + 'FLOAT8', + 'FOR', + 'FORCE', + 'FOREIGN', + 'FROM', + 'FULLTEXT', + 'GOTO', + 'GRANT', + 'GROUP', + 'HAVING', + 'HIGH_PRIORITY', + 'HOUR_MICROSECOND', + 'HOUR_MINUTE', + 'HOUR_SECOND', + 'IF', + 'IGNORE', + 'IN', + 'INDEX', + 'INFILE', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INT', + 'INT1', + 'INT2', + 'INT3', + 'INT4', + 'INT8', + 'INTEGER', + 'INTERVAL', + 'INTO', + 'IS', + 'ITERATE', + 'JOIN', + 'KEY', + 'KEYS', + 'KILL', + 'LABEL', + 'LEADING', + 'LEAVE', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LINES', + 'LOAD', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'LOCK', + 'LONG', + 'LONGBLOB', + 'LONGTEXT', + 'LOOP', + 'LOW_PRIORITY', + 'MATCH', + 'MEDIUMBLOB', + 'MEDIUMINT', + 'MEDIUMTEXT', + 'MIDDLEINT', + 'MINUTE_MICROSECOND', + 'MINUTE_SECOND', + 'MOD', + 'MODIFIES', + 'NATURAL', + 'NOT', + 'NO_WRITE_TO_BINLOG', + 'NULL', + 'NUMERIC', + 'ON', + 'OPTIMIZE', + 'OPTION', + 'OPTIONALLY', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OUTFILE', + 'PRECISION', + 'PRIMARY', + 'PROCEDURE', + 'PURGE', + 'RAID0', + 'READ', + 'READS', + 'REAL', + 'REFERENCES', + 'REGEXP', + 'RELEASE', + 'RENAME', + 'REPEAT', + 'REPLACE', + 'REQUIRE', + 'RESTRICT', + 'RETURN', + 'REVOKE', + 'RIGHT', + 'RLIKE', + 'SCHEMA', + 'SCHEMAS', + 'SECOND_MICROSECOND', + 'SELECT', + 'SENSITIVE', + 'SEPARATOR', + 'SET', + 'SHOW', + 'SMALLINT', + 'SONAME', + 'SPATIAL', + 'SPECIFIC', + 'SQL', + 'SQLEXCEPTION', + 'SQLSTATE', + 'SQLWARNING', + 'SQL_BIG_RESULT', + 'SQL_CALC_FOUND_ROWS', + 'SQL_SMALL_RESULT', + 'SSL', + 'STARTING', + 'STRAIGHT_JOIN', + 'TABLE', + 'TERMINATED', + 'THEN', + 'TINYBLOB', + 'TINYINT', + 'TINYTEXT', + 'TO', + 'TRAILING', + 'TRIGGER', + 'TRUE', + 'UNDO', + 'UNION', + 'UNIQUE', + 'UNLOCK', + 'UNSIGNED', + 'UPDATE', + 'USAGE', + 'USE', + 'USING', + 'UTC_DATE', + 'UTC_TIME', + 'UTC_TIMESTAMP', + 'VALUES', + 'VARBINARY', + 'VARCHAR', + 'VARCHARACTER', + 'VARYING', + 'WHEN', + 'WHERE', + 'WHILE', + 'WITH', + 'WRITE', + 'X509', + 'XOR', + 'YEAR_MONTH', + 'ZEROFILL', + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php new file mode 100644 index 0000000..a5840a0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php @@ -0,0 +1,156 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Oracle Keywordlist + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + */ +class OracleKeywords extends KeywordList +{ + public function getName() + { + return 'Oracle'; + } + + protected function getKeywords() + { + return array( + 'ACCESS', + 'ELSE', + 'MODIFY', + 'START', + 'ADD', + 'EXCLUSIVE', + 'NOAUDIT', + 'SELECT', + 'ALL', + 'EXISTS', + 'NOCOMPRESS', + 'SESSION', + 'ALTER', + 'FILE', + 'NOT', + 'SET', + 'AND', + 'FLOAT', + 'NOTFOUND ', + 'SHARE', + 'ANY', + 'FOR', + 'NOWAIT', + 'SIZE', + 'ARRAYLEN', + 'FROM', + 'NULL', + 'SMALLINT', + 'AS', + 'GRANT', + 'NUMBER', + 'SQLBUF', + 'ASC', + 'GROUP', + 'OF', + 'SUCCESSFUL', + 'AUDIT', + 'HAVING', + 'OFFLINE ', + 'SYNONYM', + 'BETWEEN', + 'IDENTIFIED', + 'ON', + 'SYSDATE', + 'BY', + 'IMMEDIATE', + 'ONLINE', + 'TABLE', + 'CHAR', + 'IN', + 'OPTION', + 'THEN', + 'CHECK', + 'INCREMENT', + 'OR', + 'TO', + 'CLUSTER', + 'INDEX', + 'ORDER', + 'TRIGGER', + 'COLUMN', + 'INITIAL', + 'PCTFREE', + 'UID', + 'COMMENT', + 'INSERT', + 'PRIOR', + 'UNION', + 'COMPRESS', + 'INTEGER', + 'PRIVILEGES', + 'UNIQUE', + 'CONNECT', + 'INTERSECT', + 'PUBLIC', + 'UPDATE', + 'CREATE', + 'INTO', + 'RAW', + 'USER', + 'CURRENT', + 'IS', + 'RENAME', + 'VALIDATE', + 'DATE', + 'LEVEL', + 'RESOURCE', + 'VALUES', + 'DECIMAL', + 'LIKE', + 'REVOKE', + 'VARCHAR', + 'DEFAULT', + 'LOCK', + 'ROW', + 'VARCHAR2', + 'DELETE', + 'LONG', + 'ROWID', + 'VIEW', + 'DESC', + 'MAXEXTENTS', + 'ROWLABEL', + 'WHENEVER', + 'DISTINCT', + 'MINUS', + 'ROWNUM', + 'WHERE', + 'DROP', + 'MODE', + 'ROWS', + 'WITH', + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php new file mode 100644 index 0000000..e5acea0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php @@ -0,0 +1,131 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * PostgreSQL Keywordlist + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author Marcelo Santos Araujo + */ +class PostgreSQLKeywords extends KeywordList +{ + public function getName() + { + return 'PostgreSQL'; + } + + protected function getKeywords() + { + return array( + 'ALL', + 'ANALYSE', + 'ANALYZE', + 'AND', + 'ANY', + 'AS', + 'ASC', + 'AUTHORIZATION', + 'BETWEEN', + 'BINARY', + 'BOTH', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONSTRAINT', + 'CREATE', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'DEFAULT', + 'DEFERRABLE', + 'DESC', + 'DISTINCT', + 'DO', + 'ELSE', + 'END', + 'EXCEPT', + 'FALSE', + 'FOR', + 'FOREIGN', + 'FREEZE', + 'FROM', + 'FULL', + 'GRANT', + 'GROUP', + 'HAVING', + 'ILIKE', + 'IN', + 'INITIALLY', + 'INNER', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'LEADING', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'NATURAL', + 'NEW', + 'NOT', + 'NOTNULL', + 'NULL', + 'OFF', + 'OFFSET', + 'OLD', + 'ON', + 'ONLY', + 'OR', + 'ORDER', + 'OUTER', + 'OVERLAPS', + 'PLACING', + 'PRIMARY', + 'REFERENCES', + 'SELECT', + 'SESSION_USER', + 'SIMILAR', + 'SOME', + 'TABLE', + 'THEN', + 'TO', + 'TRAILING', + 'TRUE', + 'UNION', + 'UNIQUE', + 'USER', + 'USING', + 'VERBOSE', + 'WHEN', + 'WHERE' + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php new file mode 100644 index 0000000..5c99ba3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php @@ -0,0 +1,116 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +class ReservedKeywordsValidator implements Visitor +{ + /** + * @var KeywordList[] + */ + private $keywordLists = array(); + + /** + * @var array + */ + private $violations = array(); + + public function __construct(array $keywordLists) + { + $this->keywordLists = $keywordLists; + } + + public function getViolations() + { + return $this->violations; + } + + /** + * @param string $word + * @return array + */ + private function isReservedWord($word) + { + if ($word[0] == "`") { + $word = str_replace('`', '', $word); + } + + $keywordLists = array(); + foreach ($this->keywordLists AS $keywordList) { + if ($keywordList->isKeyword($word)) { + $keywordLists[] = $keywordList->getName(); + } + } + return $keywordLists; + } + + private function addViolation($asset, $violatedPlatforms) + { + if (!$violatedPlatforms) { + return; + } + + $this->violations[] = $asset . ' keyword violations: ' . implode(', ', $violatedPlatforms); + } + + public function acceptColumn(Table $table, Column $column) + { + $this->addViolation( + 'Table ' . $table->getName() . ' column ' . $column->getName(), + $this->isReservedWord($column->getName()) + ); + } + + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + + } + + public function acceptIndex(Table $table, Index $index) + { + + } + + public function acceptSchema(Schema $schema) + { + + } + + public function acceptSequence(Sequence $sequence) + { + + } + + public function acceptTable(Table $table) + { + $this->addViolation( + 'Table ' . $table->getName(), + $this->isReservedWord($table->getName()) + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php new file mode 100644 index 0000000..4d12aff --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php @@ -0,0 +1,164 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SQLite Keywords + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class SQLiteKeywords extends KeywordList +{ + public function getName() + { + return 'SQLite'; + } + + protected function getKeywords() + { + return array( + 'ABORT', + 'ACTION', + 'ADD', + 'AFTER', + 'ALL', + 'ALTER', + 'ANALYZE', + 'AND', + 'AS', + 'ASC', + 'ATTACH', + 'AUTOINCREMENT', + 'BEFORE', + 'BEGIN', + 'BETWEEN', + 'BY', + 'CASCADE', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'COMMIT', + 'CONFLICT', + 'CONSTRAINT', + 'CREATE', + 'CROSS', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'DATABASE', + 'DEFAULT', + 'DEFERRABLE', + 'DEFERRED', + 'DELETE', + 'DESC', + 'DETACH', + 'DISTINCT', + 'DROP', + 'EACH', + 'ELSE', + 'END', + 'ESCAPE', + 'EXCEPT', + 'EXCLUSIVE', + 'EXISTS', + 'EXPLAIN', + 'FAIL', + 'FOR', + 'FOREIGN', + 'FROM', + 'FULL', + 'GLOB', + 'GROUP', + 'HAVING', + 'IF', + 'IGNORE', + 'IMMEDIATE', + 'IN', + 'INDEX', + 'INDEXED', + 'INITIALLY', + 'INNER', + 'INSERT', + 'INSTEAD', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'KEY', + 'LEFT', + 'LIKE', + 'LIMIT', + 'MATCH', + 'NATURAL', + 'NO', + 'NOT', + 'NOTNULL', + 'NULL', + 'OF', + 'OFFSET', + 'ON', + 'OR', + 'ORDER', + 'OUTER', + 'PLAN', + 'PRAGMA', + 'PRIMARY', + 'QUERY', + 'RAISE', + 'REFERENCES', + 'REGEXP', + 'REINDEX', + 'RELEASE', + 'RENAME', + 'REPLACE', + 'RESTRICT', + 'RIGHT', + 'ROLLBACK', + 'ROW', + 'SAVEPOINT', + 'SELECT', + 'SET', + 'TABLE', + 'TEMP', + 'TEMPORARY', + 'THEN', + 'TO', + 'TRANSACTION', + 'TRIGGER', + 'UNION', + 'UNIQUE', + 'UPDATE', + 'USING', + 'VACUUM', + 'VALUES', + 'VIEW', + 'VIRTUAL', + 'WHEN', + 'WHERE' + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php new file mode 100644 index 0000000..8520e45 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -0,0 +1,768 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException, + Doctrine\DBAL\Schema\TableDiff, + Doctrine\DBAL\Schema\Index, + Doctrine\DBAL\Schema\Table; + +/** + * The MySqlPlatform provides the behavior, features and SQL dialect of the + * MySQL database platform. This platform represents a MySQL 5.0 or greater platform that + * uses the InnoDB storage engine. + * + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + * @todo Rename: MySQLPlatform + */ +class MySqlPlatform extends AbstractPlatform +{ + /** + * Gets the character used for identifier quoting. + * + * @return string + * @override + */ + public function getIdentifierQuoteCharacter() + { + return '`'; + } + + /** + * Returns the regular expression operator. + * + * @return string + * @override + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } + + /** + * Returns global unique identifier + * + * @return string to get global unique identifier + * @override + */ + public function getGuidExpression() + { + return 'UUID()'; + } + + /** + * returns the position of the first occurrence of substring $substr in string $str + * + * @param string $substr literal string to find + * @param string $str literal string + * @param int $pos position to start at, beginning of string by default + * @return integer + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } else { + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + } + + /** + * Returns a series of strings concatinated + * + * concat() accepts an arbitrary number of parameters. Each parameter + * must contain an expression or an array with expressions. + * + * @param string|array(string) strings that will be concatinated. + * @override + */ + public function getConcatExpression() + { + $args = func_get_args(); + return 'CONCAT(' . join(', ', (array) $args) . ')'; + } + + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; + } + + public function getDateAddDaysExpression($date, $days) + { + return 'DATE_ADD(' . $date . ', INTERVAL ' . $days . ' DAY)'; + } + + public function getDateSubDaysExpression($date, $days) + { + return 'DATE_SUB(' . $date . ', INTERVAL ' . $days . ' DAY)'; + } + + public function getDateAddMonthExpression($date, $months) + { + return 'DATE_ADD(' . $date . ', INTERVAL ' . $months . ' MONTH)'; + } + + public function getDateSubMonthExpression($date, $months) + { + return 'DATE_SUB(' . $date . ', INTERVAL ' . $months . ' MONTH)'; + } + + public function getListDatabasesSQL() + { + return 'SHOW DATABASES'; + } + + public function getListTableConstraintsSQL($table) + { + return 'SHOW INDEX FROM ' . $table; + } + + /** + * Two approaches to listing the table indexes. The information_schema is + * prefered, because it doesn't cause problems with SQL keywords such as "order" or "table". + * + * @param string $table + * @param string $currentDatabase + * @return string + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + if ($currentDatabase) { + return "SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, ". + "SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, ". + "CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, " . + "NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment " . + "FROM information_schema.STATISTICS WHERE TABLE_NAME = '" . $table . "' AND TABLE_SCHEMA = '" . $currentDatabase . "'"; + } else { + return 'SHOW INDEX FROM ' . $table; + } + } + + public function getListViewsSQL($database) + { + return "SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = '".$database."'"; + } + + public function getListTableForeignKeysSQL($table, $database = null) + { + $sql = "SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, ". + "k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ ". + "FROM information_schema.key_column_usage k /*!50116 ". + "INNER JOIN information_schema.referential_constraints c ON ". + " c.constraint_name = k.constraint_name AND ". + " c.table_name = '$table' */ WHERE k.table_name = '$table'"; + + if ($database) { + $sql .= " AND k.table_schema = '$database' /*!50116 AND c.constraint_schema = '$database' */"; + } + + $sql .= " AND k.`REFERENCED_COLUMN_NAME` is not NULL"; + + return $sql; + } + + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * Gets the SQL snippet used to declare a VARCHAR column on the MySql platform. + * + * @params array $field + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** @override */ + public function getClobTypeDeclarationSQL(array $field) + { + if ( ! empty($field['length']) && is_numeric($field['length'])) { + $length = $field['length']; + if ($length <= 255) { + return 'TINYTEXT'; + } else if ($length <= 65532) { + return 'TEXT'; + } else if ($length <= 16777215) { + return 'MEDIUMTEXT'; + } + } + return 'LONGTEXT'; + } + + /** + * @override + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return 'TIMESTAMP'; + } else { + return 'DATETIME'; + } + } + + /** + * @override + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * @override + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * @override + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'TINYINT(1)'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param string $collation name of the collation + * @return string DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration. + */ + public function getCollationFieldDeclaration($collation) + { + return 'COLLATE ' . $collation; + } + + /** + * Whether the platform prefers identity columns for ID generation. + * MySql prefers "autoincrement" identity columns since sequences can only + * be emulated with a table. + * + * @return boolean + * @override + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * Whether the platform supports identity columns. + * MySql supports this through AUTO_INCREMENT columns. + * + * @return boolean + * @override + */ + public function supportsIdentityColumns() + { + return true; + } + + public function supportsInlineColumnComments() + { + return true; + } + + public function getShowDatabasesSQL() + { + return 'SHOW DATABASES'; + } + + public function getListTablesSQL() + { + return "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"; + } + + public function getListTableColumnsSQL($table, $database = null) + { + if ($database) { + return "SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, ". + "COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, " . + "CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS CollactionName ". + "FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '" . $database . "' AND TABLE_NAME = '" . $table . "'"; + } else { + return 'DESCRIBE ' . $table; + } + } + + /** + * create a new database + * + * @param string $name name of the database that should be created + * @return string + * @override + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * drop an existing database + * + * @param string $name name of the database that should be dropped + * @return string + * @override + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * create a new table + * + * @param string $tableName Name of the database that should be created + * @param array $columns Associative array that contains the definition of each field of the new table + * The indexes of the array entries are the names of the fields of the table an + * the array entry values are associative arrays like those that are meant to be + * passed with the field definitions to get[Type]Declaration() functions. + * array( + * 'id' => array( + * 'type' => 'integer', + * 'unsigned' => 1 + * 'notnull' => 1 + * 'default' => 0 + * ), + * 'name' => array( + * 'type' => 'text', + * 'length' => 12 + * ), + * 'password' => array( + * 'type' => 'text', + * 'length' => 12 + * ) + * ); + * @param array $options An associative array of table options: + * array( + * 'comment' => 'Foo', + * 'charset' => 'utf8', + * 'collate' => 'utf8_unicode_ci', + * 'engine' => 'innodb', + * 'foreignKeys' => array( + * new ForeignKeyConstraint(), + * new ForeignKeyConstraint(), + * new ForeignKeyConstraint(), + * // etc + * ) + * ); + * + * @return void + * @override + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $index => $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition); + } + } + + // add all indexes + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach($options['indexes'] as $index => $definition) { + $queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition); + } + } + + // attach all primary keys + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE '; + if (!empty($options['temporary'])) { + $query .= 'TEMPORARY '; + } + $query.= 'TABLE ' . $tableName . ' (' . $queryFields . ')'; + + $optionStrings = array(); + + if (isset($options['comment'])) { + $optionStrings['comment'] = 'COMMENT = ' . $options['comment']; + } + if (isset($options['charset'])) { + $optionStrings['charset'] = 'DEFAULT CHARACTER SET ' . $options['charset']; + if (isset($options['collate'])) { + $optionStrings['charset'] .= ' COLLATE ' . $options['collate']; + } + } + + // get the type of the table + if (isset($options['engine'])) { + $optionStrings[] = 'ENGINE = ' . $options['engine']; + } else { + // default to innodb + $optionStrings[] = 'ENGINE = InnoDB'; + } + + if ( ! empty($optionStrings)) { + $query.= ' '.implode(' ', $optionStrings); + } + $sql[] = $query; + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * Gets the SQL to alter an existing table. + * + * @param TableDiff $diff + * @return array + */ + public function getAlterTableSQL(TableDiff $diff) + { + $columnSql = array(); + $queryParts = array(); + if ($diff->newName !== false) { + $queryParts[] = 'RENAME TO ' . $diff->newName; + } + + foreach ($diff->addedColumns AS $fieldName => $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->removedColumns AS $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP ' . $column->getQuotedName($this); + } + + foreach ($diff->changedColumns AS $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + /* @var $columnDiff Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . ($columnDiff->oldColumnName) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->renamedColumns AS $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . $oldColumnName . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + $sql = array(); + $tableSql = array(); + + if (!$this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . implode(", ", $queryParts); + } + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Fix for DROP/CREATE index after foreign key change from OneToOne to ManyToOne + * + * @param TableDiff $diff + * @return array + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $table = $diff->name; + + foreach ($diff->removedIndexes AS $remKey => $remIndex) { + + foreach ($diff->addedIndexes as $addKey => $addIndex) { + if ($remIndex->getColumns() == $addIndex->getColumns()) { + + $columns = $addIndex->getColumns(); + $type = ''; + if ($addIndex->isUnique()) { + $type = 'UNIQUE '; + } + + $query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', '; + $query .= 'ADD ' . $type . 'INDEX ' . $addIndex->getName(); + $query .= ' (' . $this->getIndexFieldDeclarationListSQL($columns) . ')'; + + $sql[] = $query; + + unset($diff->removedIndexes[$remKey]); + unset($diff->addedIndexes[$addKey]); + + break; + } + } + } + + $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff)); + + return $sql; + } + + /** + * Obtain DBMS specific SQL code portion needed to declare an integer type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param string $field associative array with the name of the properties + * of the field being declared as array indexes. + * Currently, the types of supported field + * properties are as follows: + * + * unsigned + * Boolean flag that indicates whether the field + * should be declared as unsigned integer if + * possible. + * + * default + * Integer value to be used as default for this + * field. + * + * notnull + * Boolean flag that indicates whether this field is + * constrained to not be set to null. + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + * @override + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** @override */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** @override */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** @override */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' AUTO_INCREMENT'; + } + $unsigned = (isset($columnDef['unsigned']) && $columnDef['unsigned']) ? ' UNSIGNED' : ''; + + return $unsigned . $autoinc; + } + + /** + * Return the FOREIGN KEY query section dealing with non-standard options + * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... + * + * @param ForeignKeyConstraint $foreignKey + * @return string + * @override + */ + public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey) + { + $query = ''; + if ($foreignKey->hasOption('match')) { + $query .= ' MATCH ' . $foreignKey->getOption('match'); + } + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + return $query; + } + + /** + * Gets the SQL to drop an index of a table. + * + * @param Index $index name of the index to be dropped + * @param string|Table $table name of table that should be used in method + * @override + */ + public function getDropIndexSQL($index, $table=null) + { + if($index instanceof Index) { + $indexName = $index->getQuotedName($this); + } else if(is_string($index)) { + $indexName = $index; + } else { + throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + if($table instanceof Table) { + $table = $table->getQuotedName($this); + } else if(!is_string($table)) { + throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + if ($index instanceof Index && $index->isPrimary()) { + // mysql primary keys are always named "PRIMARY", + // so we cannot use them in statements because of them being keyword. + return $this->getDropPrimaryKeySQL($table); + } + + return 'DROP INDEX ' . $indexName . ' ON ' . $table; + } + + /** + * @param Index $index + * @param Table $table + */ + protected function getDropPrimaryKeySQL($table) + { + return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + } + + public function getSetTransactionIsolationSQL($level) + { + return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * Get the platform name for this instance. + * + * @return string + */ + public function getName() + { + return 'mysql'; + } + + public function getReadLockSQL() + { + return 'LOCK IN SHARE MODE'; + } + + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'tinyint' => 'boolean', + 'smallint' => 'smallint', + 'mediumint' => 'integer', + 'int' => 'integer', + 'integer' => 'integer', + 'bigint' => 'bigint', + 'tinytext' => 'text', + 'mediumtext' => 'text', + 'longtext' => 'text', + 'text' => 'text', + 'varchar' => 'string', + 'string' => 'string', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'time' => 'time', + 'float' => 'float', + 'double' => 'float', + 'real' => 'float', + 'decimal' => 'decimal', + 'numeric' => 'decimal', + 'year' => 'date', + 'longblob' => 'blob', + 'blob' => 'blob', + 'mediumblob' => 'blob', + 'tinyblob' => 'blob', + ); + } + + public function getVarcharMaxLength() + { + return 65535; + } + + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\MySQLKeywords'; + } + + /** + * Get SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction. + * + * MySQL commits a transaction implicitly when DROP TABLE is executed, however not + * if DROP TEMPORARY TABLE is executed. + * + * @throws \InvalidArgumentException + * @param $table + * @return string + */ + public function getDropTemporaryTableSQL($table) + { + if ($table instanceof \Doctrine\DBAL\Schema\Table) { + $table = $table->getQuotedName($this); + } else if(!is_string($table)) { + throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + return 'DROP TEMPORARY TABLE ' . $table; + } + + /** + * Gets the SQL Snippet used to declare a BLOB column type. + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'LONGBLOB'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php new file mode 100644 index 0000000..215ea1c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php @@ -0,0 +1,840 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\TableDiff; + +/** + * OraclePlatform. + * + * @since 2.0 + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + */ +class OraclePlatform extends AbstractPlatform +{ + /** + * return string to call a function to get a substring inside an SQL statement + * + * Note: Not SQL92, but common functionality. + * + * @param string $value an sql string literal or column name/alias + * @param integer $position where to start the substring portion + * @param integer $length the substring portion length + * @return string SQL substring function with given parameters + * @override + */ + public function getSubstringExpression($value, $position, $length = null) + { + if ($length !== null) { + return "SUBSTR($value, $position, $length)"; + } + + return "SUBSTR($value, $position)"; + } + + /** + * Return string to call a variable with the current timestamp inside an SQL statement + * There are three special variables for current date and time: + * - CURRENT_TIMESTAMP (date and time, TIMESTAMP type) + * - CURRENT_DATE (date, DATE type) + * - CURRENT_TIME (time, TIME type) + * + * @return string to call a variable with the current timestamp + * @override + */ + public function getNowExpression($type = 'timestamp') + { + switch ($type) { + case 'date': + case 'time': + case 'timestamp': + default: + return 'TO_CHAR(CURRENT_TIMESTAMP, \'YYYY-MM-DD HH24:MI:SS\')'; + } + } + + /** + * returns the position of the first occurrence of substring $substr in string $str + * + * @param string $substr literal string to find + * @param string $str literal string + * @param int $pos position to start at, beginning of string by default + * @return integer + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'INSTR('.$str.', '.$substr.')'; + } else { + return 'INSTR('.$str.', '.$substr.', '.$startPos.')'; + } + } + + /** + * Returns global unique identifier + * + * @return string to get global unique identifier + * @override + */ + public function getGuidExpression() + { + return 'SYS_GUID()'; + } + + /** + * Get the number of days difference between two dates. + * + * Note: Since Oracle timestamp differences are calculated down to the microsecond we have to truncate + * them to the difference in days. This is obviously a restriction of the original functionality, but we + * need to make this a portable function. + * + * @param type $date1 + * @param type $date2 + * @return type + */ + public function getDateDiffExpression($date1, $date2) + { + return "TRUNC(TO_NUMBER(SUBSTR((" . $date1 . "-" . $date2 . "), 1, INSTR(" . $date1 . "-" . $date2 .", ' '))))"; + } + + /** + * {@inheritdoc} + */ + public function getDateAddDaysExpression($date, $days) + { + return '(' . $date . '+' . $days . ')'; + } + + /** + * {@inheritdoc} + */ + public function getDateSubDaysExpression($date, $days) + { + return '(' . $date . '-' . $days . ')'; + } + + /** + * {@inheritdoc} + */ + public function getDateAddMonthExpression($date, $months) + { + return "ADD_MONTHS(" . $date . ", " . $months . ")"; + } + + /** + * {@inheritdoc} + */ + public function getDateSubMonthExpression($date, $months) + { + return "ADD_MONTHS(" . $date . ", -" . $months . ")"; + } + + /** + * {@inheritdoc} + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return 'BITAND('.$value1 . ', ' . $value2 . ')'; + } + + /** + * {@inheritdoc} + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return '(' . $value1 . '-' . + $this->getBitAndComparisonExpression($value1, $value2) + . '+' . $value2 . ')'; + } + + /** + * Gets the SQL used to create a sequence that starts with a given value + * and increments by the given allocation size. + * + * Need to specifiy minvalue, since start with is hidden in the system and MINVALUE <= START WITH. + * Therefore we can use MINVALUE to be able to get a hint what START WITH was for later introspection + * in {@see listSequences()} + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * @return string + */ + public function getCreateSequenceSQL(\Doctrine\DBAL\Schema\Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' START WITH ' . $sequence->getInitialValue() . + ' MINVALUE ' . $sequence->getInitialValue() . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + public function getAlterSequenceSQL(\Doctrine\DBAL\Schema\Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * {@inheritdoc} + * + * @param string $sequenceName + * @override + */ + public function getSequenceNextValSQL($sequenceName) + { + return 'SELECT ' . $sequenceName . '.nextval FROM DUAL'; + } + + /** + * {@inheritdoc} + * + * @param integer $level + * @override + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case \Doctrine\DBAL\Connection::TRANSACTION_READ_UNCOMMITTED: + return 'READ UNCOMMITTED'; + case \Doctrine\DBAL\Connection::TRANSACTION_READ_COMMITTED: + return 'READ COMMITTED'; + case \Doctrine\DBAL\Connection::TRANSACTION_REPEATABLE_READ: + case \Doctrine\DBAL\Connection::TRANSACTION_SERIALIZABLE: + return 'SERIALIZABLE'; + default: + return parent::_getTransactionIsolationLevelSQL($level); + } + } + + /** + * @override + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'NUMBER(1)'; + } + + /** + * @override + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'NUMBER(10)'; + } + + /** + * @override + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'NUMBER(20)'; + } + + /** + * @override + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'NUMBER(5)'; + } + + /** + * @override + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0)'; + } + + /** + * @override + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITH TIME ZONE'; + } + + /** + * @override + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * @override + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * @override + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return ''; + } + + /** + * Gets the SQL snippet used to declare a VARCHAR column on the Oracle platform. + * + * @params array $field + * @override + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(2000)') + : ($length ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)'); + } + + /** @override */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'CLOB'; + } + + public function getListDatabasesSQL() + { + return 'SELECT username FROM all_users'; + } + + public function getListSequencesSQL($database) + { + return "SELECT sequence_name, min_value, increment_by FROM sys.all_sequences ". + "WHERE SEQUENCE_OWNER = '".strtoupper($database)."'"; + } + + /** + * + * @param string $table + * @param array $columns + * @param array $options + * @return array + */ + protected function _getCreateTableSQL($table, array $columns, array $options = array()) + { + $indexes = isset($options['indexes']) ? $options['indexes'] : array(); + $options['indexes'] = array(); + $sql = parent::_getCreateTableSQL($table, $columns, $options); + + foreach ($columns as $name => $column) { + if (isset($column['sequence'])) { + $sql[] = $this->getCreateSequenceSQL($column['sequence'], 1); + } + + if (isset($column['autoincrement']) && $column['autoincrement'] || + (isset($column['autoinc']) && $column['autoinc'])) { + $sql = array_merge($sql, $this->getCreateAutoincrementSql($name, $table)); + } + } + + if (isset($indexes) && ! empty($indexes)) { + foreach ($indexes as $indexName => $index) { + $sql[] = $this->getCreateIndexSQL($index, $table); + } + } + + return $sql; + } + + /** + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaOracleReader.html + * @param string $table + * @return string + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $table = strtoupper($table); + + return "SELECT uind.index_name AS name, " . + " uind.index_type AS type, " . + " decode( uind.uniqueness, 'NONUNIQUE', 0, 'UNIQUE', 1 ) AS is_unique, " . + " uind_col.column_name AS column_name, " . + " uind_col.column_position AS column_pos, " . + " (SELECT ucon.constraint_type FROM user_constraints ucon WHERE ucon.constraint_name = uind.index_name) AS is_primary ". + "FROM user_indexes uind, user_ind_columns uind_col " . + "WHERE uind.index_name = uind_col.index_name AND uind_col.table_name = '$table' ORDER BY uind_col.column_position ASC"; + } + + public function getListTablesSQL() + { + return 'SELECT * FROM sys.user_tables'; + } + + public function getListViewsSQL($database) + { + return 'SELECT view_name, text FROM sys.user_views'; + } + + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + public function getCreateAutoincrementSql($name, $table, $start = 1) + { + $table = strtoupper($table); + $sql = array(); + + $indexName = $table . '_AI_PK'; + $definition = array( + 'primary' => true, + 'columns' => array($name => true), + ); + + $idx = new \Doctrine\DBAL\Schema\Index($indexName, array($name), true, true); + + $sql[] = 'DECLARE + constraints_Count NUMBER; +BEGIN + SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = \''.$table.'\' AND CONSTRAINT_TYPE = \'P\'; + IF constraints_Count = 0 OR constraints_Count = \'\' THEN + EXECUTE IMMEDIATE \''.$this->getCreateConstraintSQL($idx, $table).'\'; + END IF; +END;'; + + $sequenceName = $table . '_SEQ'; + $sequence = new \Doctrine\DBAL\Schema\Sequence($sequenceName, $start); + $sql[] = $this->getCreateSequenceSQL($sequence); + + $triggerName = $table . '_AI_PK'; + $sql[] = 'CREATE TRIGGER ' . $triggerName . ' + BEFORE INSERT + ON ' . $table . ' + FOR EACH ROW +DECLARE + last_Sequence NUMBER; + last_InsertID NUMBER; +BEGIN + SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $name . ' FROM DUAL; + IF (:NEW.' . $name . ' IS NULL OR :NEW.'.$name.' = 0) THEN + SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $name . ' FROM DUAL; + ELSE + SELECT NVL(Last_Number, 0) INTO last_Sequence + FROM User_Sequences + WHERE Sequence_Name = \'' . $sequenceName . '\'; + SELECT :NEW.' . $name . ' INTO last_InsertID FROM DUAL; + WHILE (last_InsertID > last_Sequence) LOOP + SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; + END LOOP; + END IF; +END;'; + return $sql; + } + + public function getDropAutoincrementSql($table) + { + $table = strtoupper($table); + $trigger = $table . '_AI_PK'; + + if ($trigger) { + $sql[] = 'DROP TRIGGER ' . $trigger; + $sql[] = $this->getDropSequenceSQL($table.'_SEQ'); + + $indexName = $table . '_AI_PK'; + $sql[] = $this->getDropConstraintSQL($indexName, $table); + } + + return $sql; + } + + public function getListTableForeignKeysSQL($table) + { + $table = strtoupper($table); + + return "SELECT alc.constraint_name, + alc.DELETE_RULE, + alc.search_condition, + cols.column_name \"local_column\", + cols.position, + r_alc.table_name \"references_table\", + r_cols.column_name \"foreign_column\" + FROM user_cons_columns cols +LEFT JOIN user_constraints alc + ON alc.constraint_name = cols.constraint_name +LEFT JOIN user_constraints r_alc + ON alc.r_constraint_name = r_alc.constraint_name +LEFT JOIN user_cons_columns r_cols + ON r_alc.constraint_name = r_cols.constraint_name + AND cols.position = r_cols.position + WHERE alc.constraint_name = cols.constraint_name + AND alc.constraint_type = 'R' + AND alc.table_name = '".$table."'"; + } + + public function getListTableConstraintsSQL($table) + { + $table = strtoupper($table); + return 'SELECT * FROM user_constraints WHERE table_name = \'' . $table . '\''; + } + + public function getListTableColumnsSQL($table, $database = null) + { + $table = strtoupper($table); + + $tabColumnsTableName = "user_tab_columns"; + $ownerCondition = ''; + if(null !== $database){ + $database = strtoupper($database); + $tabColumnsTableName = "all_tab_columns"; + $ownerCondition = "AND c.owner = '".$database."'"; + } + + return "SELECT c.*, d.comments FROM $tabColumnsTableName c ". + "INNER JOIN user_col_comments d ON d.TABLE_NAME = c.TABLE_NAME AND d.COLUMN_NAME = c.COLUMN_NAME ". + "WHERE c.table_name = '" . $table . "' ".$ownerCondition." ORDER BY c.column_name"; + } + + /** + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * @return string + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof \Doctrine\DBAL\Schema\Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * @param ForeignKeyConstraint|string $foreignKey + * @param Table|string $table + * @return string + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if ($foreignKey instanceof \Doctrine\DBAL\Schema\ForeignKeyConstraint) { + $foreignKey = $foreignKey->getQuotedName($this); + } + + if ($table instanceof \Doctrine\DBAL\Schema\Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; + } + + public function getDropDatabaseSQL($database) + { + return 'DROP USER ' . $database . ' CASCADE'; + } + + /** + * Gets the sql statements for altering an existing table. + * + * The method returns an array of sql statements, since some platforms need several statements. + * + * @param string $diff->name name of the table that is intended to be changed. + * @param array $changes associative array that contains the details of each type * + * @param boolean $check indicates whether the function should just check if the DBMS driver + * can perform the requested table alterations if the value is true or + * actually perform them otherwise. + * @return array + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $commentsSQL = array(); + $columnSql = array(); + + $fields = array(); + foreach ($diff->addedColumns AS $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $fields[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + if ($comment = $this->getColumnComment($column)) { + $commentsSQL[] = $this->getCommentOnColumnSQL($diff->name, $column->getName(), $comment); + } + } + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ADD (' . implode(', ', $fields) . ')'; + } + + $fields = array(); + foreach ($diff->changedColumns AS $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $column = $columnDiff->column; + $fields[] = $column->getQuotedName($this). ' ' . $this->getColumnDeclarationSQL('', $column->toArray()); + if ($columnDiff->hasChanged('comment') && $comment = $this->getColumnComment($column)) { + $commentsSQL[] = $this->getCommentOnColumnSQL($diff->name, $column->getName(), $comment); + } + } + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' MODIFY (' . implode(', ', $fields) . ')'; + } + + foreach ($diff->renamedColumns AS $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $sql[] = 'ALTER TABLE ' . $diff->name . ' RENAME COLUMN ' . $oldColumnName .' TO ' . $column->getQuotedName($this); + } + + $fields = array(); + foreach ($diff->removedColumns AS $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $fields[] = $column->getQuotedName($this); + } + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' DROP (' . implode(', ', $fields).')'; + } + + $tableSql = array(); + + if (!$this->onSchemaAlterTable($diff, $tableSql)) { + if ($diff->newName !== false) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' RENAME TO ' . $diff->newName; + } + + $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff), $commentsSQL); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Whether the platform prefers sequences for ID generation. + * + * @return boolean + */ + public function prefersSequences() + { + return true; + } + + public function supportsCommentOnStatement() + { + return true; + } + + /** + * Get the platform name for this instance + * + * @return string + */ + public function getName() + { + return 'oracle'; + } + + /** + * Adds an driver-specific LIMIT clause to the query + * + * @param string $query query to modify + * @param integer $limit limit the number of rows + * @param integer $offset start reading from given offset + * @return string the modified query + */ + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + $limit = (int) $limit; + $offset = (int) $offset; + if (preg_match('/^\s*SELECT/i', $query)) { + if (!preg_match('/\sFROM\s/i', $query)) { + $query .= " FROM dual"; + } + if ($limit > 0) { + $max = $offset + $limit; + $column = '*'; + if ($offset > 0) { + $min = $offset + 1; + $query = 'SELECT * FROM (SELECT a.' . $column . ', rownum AS doctrine_rownum FROM (' . + $query . + ') a WHERE rownum <= ' . $max . ') WHERE doctrine_rownum >= ' . $min; + } else { + $query = 'SELECT a.' . $column . ' FROM (' . $query . ') a WHERE ROWNUM <= ' . $max; + } + } + } + return $query; + } + + /** + * Gets the character casing of a column in an SQL result set of this platform. + * + * Oracle returns all column names in SQL result sets in uppercase. + * + * @param string $column The column name for which to get the correct character casing. + * @return string The column name in the character casing used in SQL result sets. + */ + public function getSQLResultCasing($column) + { + return strtoupper($column); + } + + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE GLOBAL TEMPORARY TABLE"; + } + + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:sP'; + } + + public function getDateFormatString() + { + return 'Y-m-d 00:00:00'; + } + + public function getTimeFormatString() + { + return '1900-01-01 H:i:s'; + } + + public function fixSchemaElementName($schemaElementName) + { + if (strlen($schemaElementName) > 30) { + // Trim it + return substr($schemaElementName, 0, 30); + } + return $schemaElementName; + } + + /** + * Maximum length of any given databse identifier, like tables or column names. + * + * @return int + */ + public function getMaxIdentifierLength() + { + return 30; + } + + /** + * Whether the platform supports sequences. + * + * @return boolean + */ + public function supportsSequences() + { + return true; + } + + public function supportsForeignKeyOnUpdate() + { + return false; + } + + /** + * Whether the platform supports releasing savepoints. + * + * @return boolean + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * @inheritdoc + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE TABLE '.$tableName; + } + + /** + * This is for test reasons, many vendors have special requirements for dummy statements. + * + * @return string + */ + public function getDummySelectSQL() + { + return 'SELECT 1 FROM DUAL'; + } + + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'integer' => 'integer', + 'number' => 'integer', + 'pls_integer' => 'boolean', + 'binary_integer' => 'boolean', + 'varchar' => 'string', + 'varchar2' => 'string', + 'nvarchar2' => 'string', + 'char' => 'string', + 'nchar' => 'string', + 'date' => 'datetime', + 'timestamp' => 'datetime', + 'timestamptz' => 'datetimetz', + 'float' => 'float', + 'long' => 'string', + 'clob' => 'text', + 'nclob' => 'text', + 'raw' => 'text', + 'long raw' => 'text', + 'rowid' => 'string', + 'urowid' => 'string', + 'blob' => 'blob', + ); + } + + /** + * Generate SQL to release a savepoint + * + * @param string $savepoint + * @return string + */ + public function releaseSavePoint($savepoint) + { + return ''; + } + + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\OracleKeywords'; + } + + /** + * Gets the SQL Snippet used to declare a BLOB column type. + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BLOB'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php new file mode 100644 index 0000000..d398651 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php @@ -0,0 +1,792 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\TableDiff, + Doctrine\DBAL\Schema\Table; + +/** + * PostgreSqlPlatform. + * + * @since 2.0 + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @todo Rename: PostgreSQLPlatform + */ +class PostgreSqlPlatform extends AbstractPlatform +{ + /** + * Returns part of a string. + * + * Note: Not SQL92, but common functionality. + * + * @param string $value the target $value the string or the string column. + * @param int $from extract from this characeter. + * @param int $len extract this amount of characters. + * @return string sql that extracts part of a string. + * @override + */ + public function getSubstringExpression($value, $from, $len = null) + { + if ($len === null) { + return 'SUBSTR(' . $value . ', ' . $from . ')'; + } else { + return 'SUBSTR(' . $value . ', ' . $from . ', ' . $len . ')'; + } + } + + /** + * Returns the SQL string to return the current system date and time. + * + * @return string + */ + public function getNowExpression() + { + return 'LOCALTIMESTAMP(0)'; + } + + /** + * regexp + * + * @return string the regular expression operator + * @override + */ + public function getRegexpExpression() + { + return 'SIMILAR TO'; + } + + /** + * returns the position of the first occurrence of substring $substr in string $str + * + * @param string $substr literal string to find + * @param string $str literal string + * @param int $pos position to start at, beginning of string by default + * @return integer + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos !== false) { + $str = $this->getSubstringExpression($str, $startPos); + return 'CASE WHEN (POSITION('.$substr.' IN '.$str.') = 0) THEN 0 ELSE (POSITION('.$substr.' IN '.$str.') + '.($startPos-1).') END'; + } else { + return 'POSITION('.$substr.' IN '.$str.')'; + } + } + + public function getDateDiffExpression($date1, $date2) + { + return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))'; + } + + public function getDateAddDaysExpression($date, $days) + { + return "(" . $date ." + (" . $days . " || ' day')::interval)"; + } + + public function getDateSubDaysExpression($date, $days) + { + return "(" . $date ." - (" . $days . " || ' day')::interval)"; + } + + public function getDateAddMonthExpression($date, $months) + { + return "(" . $date ." + (" . $months . " || ' month')::interval)"; + } + + public function getDateSubMonthExpression($date, $months) + { + return "(" . $date ." - (" . $months . " || ' month')::interval)"; + } + + /** + * parses a literal boolean value and returns + * proper sql equivalent + * + * @param string $value boolean value to be parsed + * @return string parsed boolean value + */ + /*public function parseBoolean($value) + { + return $value; + }*/ + + /** + * Whether the platform supports sequences. + * Postgres has native support for sequences. + * + * @return boolean + */ + public function supportsSequences() + { + return true; + } + + /** + * Whether the platform supports database schemas. + * + * @return boolean + */ + public function supportsSchemas() + { + return true; + } + + /** + * Whether the platform supports identity columns. + * Postgres supports these through the SERIAL keyword. + * + * @return boolean + */ + public function supportsIdentityColumns() + { + return true; + } + + public function supportsCommentOnStatement() + { + return true; + } + + /** + * Whether the platform prefers sequences for ID generation. + * + * @return boolean + */ + public function prefersSequences() + { + return true; + } + + public function getListDatabasesSQL() + { + return 'SELECT datname FROM pg_database'; + } + + public function getListSequencesSQL($database) + { + return "SELECT + c.relname, n.nspname AS schemaname + FROM + pg_class c, pg_namespace n + WHERE relkind = 'S' AND n.oid = c.relnamespace AND + (n.nspname NOT LIKE 'pg_%' AND n.nspname != 'information_schema')"; + } + + public function getListTablesSQL() + { + return "SELECT tablename AS table_name, schemaname AS schema_name + FROM pg_tables WHERE schemaname NOT LIKE 'pg_%' AND schemaname != 'information_schema' AND tablename != 'geometry_columns' AND tablename != 'spatial_ref_sys'"; + } + + public function getListViewsSQL($database) + { + return 'SELECT viewname, definition FROM pg_views'; + } + + public function getListTableForeignKeysSQL($table, $database = null) + { + return "SELECT r.conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef + FROM pg_catalog.pg_constraint r + WHERE r.conrelid = + ( + SELECT c.oid + FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n + WHERE " .$this->getTableWhereClause($table) ." AND n.oid = c.relnamespace + ) + AND r.contype = 'f'"; + } + + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + public function getListTableConstraintsSQL($table) + { + return "SELECT + relname + FROM + pg_class + WHERE oid IN ( + SELECT indexrelid + FROM pg_index, pg_class + WHERE pg_class.relname = '$table' + AND pg_class.oid = pg_index.indrelid + AND (indisunique = 't' OR indisprimary = 't') + )"; + } + + /** + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + * @param string $table + * @return string + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "SELECT relname, pg_index.indisunique, pg_index.indisprimary, + pg_index.indkey, pg_index.indrelid + FROM pg_class, pg_index + WHERE oid IN ( + SELECT indexrelid + FROM pg_index si, pg_class sc, pg_namespace sn + WHERE " . $this->getTableWhereClause($table, 'sc', 'sn')." AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid + ) AND pg_index.indexrelid = oid"; + } + + /** + * @param string $table + * @param string $classAlias + * @param string $namespaceAlias + * @return string + */ + private function getTableWhereClause($table, $classAlias = 'c', $namespaceAlias = 'n') + { + $whereClause = $namespaceAlias.".nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND "; + if (strpos($table, ".") !== false) { + list($schema, $table) = explode(".", $table); + $schema = "'" . $schema . "'"; + } else { + $schema = "ANY(string_to_array((select setting from pg_catalog.pg_settings where name = 'search_path'),','))"; + } + $whereClause .= "$classAlias.relname = '" . $table . "' AND $namespaceAlias.nspname = $schema"; + + return $whereClause; + } + + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT + a.attnum, + a.attname AS field, + t.typname AS type, + format_type(a.atttypid, a.atttypmod) AS complete_type, + (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type, + (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM pg_catalog.pg_type t2 + WHERE t2.typtype = 'd' AND t2.typname = format_type(a.atttypid, a.atttypmod)) AS domain_complete_type, + a.attnotnull AS isnotnull, + (SELECT 't' + FROM pg_index + WHERE c.oid = pg_index.indrelid + AND pg_index.indkey[0] = a.attnum + AND pg_index.indisprimary = 't' + ) AS pri, + (SELECT pg_attrdef.adsrc + FROM pg_attrdef + WHERE c.oid = pg_attrdef.adrelid + AND pg_attrdef.adnum=a.attnum + ) AS default, + (SELECT pg_description.description + FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid + ) AS comment + FROM pg_attribute a, pg_class c, pg_type t, pg_namespace n + WHERE ".$this->getTableWhereClause($table, 'c', 'n') ." + AND a.attnum > 0 + AND a.attrelid = c.oid + AND a.atttypid = t.oid + AND n.oid = c.relnamespace + ORDER BY a.attnum"; + } + + /** + * create a new database + * + * @param string $name name of the database that should be created + * @throws PDOException + * @return void + * @override + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * drop an existing database + * + * @param string $name name of the database that should be dropped + * @throws PDOException + * @access public + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * Return the FOREIGN KEY query section dealing with non-standard options + * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey foreign key definition + * @return string + * @override + */ + public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey) + { + $query = ''; + if ($foreignKey->hasOption('match')) { + $query .= ' MATCH ' . $foreignKey->getOption('match'); + } + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + if ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) { + $query .= ' DEFERRABLE'; + } else { + $query .= ' NOT DEFERRABLE'; + } + if ($foreignKey->hasOption('feferred') && $foreignKey->getOption('feferred') !== false) { + $query .= ' INITIALLY DEFERRED'; + } else { + $query .= ' INITIALLY IMMEDIATE'; + } + return $query; + } + + /** + * generates the sql for altering an existing table on postgresql + * + * @param string $name name of the table that is intended to be changed. + * @param array $changes associative array that contains the details of each type * + * @param boolean $check indicates whether the function should just check if the DBMS driver + * can perform the requested table alterations if the value is true or + * actually perform them otherwise. + * @see Doctrine_Export::alterTable() + * @return array + * @override + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $commentsSQL = array(); + $columnSql = array(); + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $query = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + if ($comment = $this->getColumnComment($column)) { + $commentsSQL[] = $this->getCommentOnColumnSQL($diff->name, $column->getName(), $comment); + } + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $query = 'DROP ' . $column->getQuotedName($this); + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + + foreach ($diff->changedColumns AS $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $oldColumnName = $columnDiff->oldColumnName; + $column = $columnDiff->column; + + if ($columnDiff->hasChanged('type')) { + $type = $column->getType(); + + // here was a server version check before, but DBAL API does not support this anymore. + $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $type->getSqlDeclaration($column->toArray(), $this); + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + if ($columnDiff->hasChanged('default')) { + $query = 'ALTER ' . $oldColumnName . ' SET ' . $this->getDefaultValueDeclarationSQL($column->toArray()); + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + if ($columnDiff->hasChanged('notnull')) { + $query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotNull() ? 'SET' : 'DROP') . ' NOT NULL'; + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + if ($columnDiff->hasChanged('autoincrement')) { + if ($column->getAutoincrement()) { + // add autoincrement + $seqName = $diff->name . '_' . $oldColumnName . '_seq'; + + $sql[] = "CREATE SEQUENCE " . $seqName; + $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ") FROM " . $diff->name . "))"; + $query = "ALTER " . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')"; + $sql[] = "ALTER TABLE " . $diff->name . " " . $query; + } else { + // Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have + $query = "ALTER " . $oldColumnName . " " . "DROP DEFAULT"; + $sql[] = "ALTER TABLE " . $diff->name . " " . $query; + } + } + if ($columnDiff->hasChanged('comment') && $comment = $this->getColumnComment($column)) { + $commentsSQL[] = $this->getCommentOnColumnSQL($diff->name, $column->getName(), $comment); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $sql[] = 'ALTER TABLE ' . $diff->name . ' RENAME COLUMN ' . $oldColumnName . ' TO ' . $column->getQuotedName($this); + } + + $tableSql = array(); + + if (!$this->onSchemaAlterTable($diff, $tableSql)) { + if ($diff->newName !== false) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' RENAME TO ' . $diff->newName; + } + + $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff), $commentsSQL); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Gets the SQL to create a sequence on this platform. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * @return string + */ + public function getCreateSequenceSQL(\Doctrine\DBAL\Schema\Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' MINVALUE ' . $sequence->getInitialValue() . + ' START ' . $sequence->getInitialValue(); + } + + public function getAlterSequenceSQL(\Doctrine\DBAL\Schema\Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * Drop existing sequence + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * @return string + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof \Doctrine\DBAL\Schema\Sequence) { + $sequence = $sequence->getQuotedName($this); + } + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * @param ForeignKeyConstraint|string $foreignKey + * @param Table|string $table + * @return string + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + return $this->getDropConstraintSQL($foreignKey, $table); + } + + /** + * Gets the SQL used to create a table. + * + * @param unknown_type $tableName + * @param array $columns + * @param array $options + * @return unknown + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $queryFields . ')'; + + $sql[] = $query; + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] AS $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + } + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * Postgres wants boolean values converted to the strings 'true'/'false'. + * + * @param array $item + * @override + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + if (is_bool($value) || is_numeric($item)) { + $item[$key] = ($value) ? 'true' : 'false'; + } + } + } else { + if (is_bool($item) || is_numeric($item)) { + $item = ($item) ? 'true' : 'false'; + } + } + return $item; + } + + public function getSequenceNextValSQL($sequenceName) + { + return "SELECT NEXTVAL('" . $sequenceName . "')"; + } + + public function getSetTransactionIsolationSQL($level) + { + return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ' + . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * @override + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BOOLEAN'; + } + + /** + * @override + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'SERIAL'; + } + + return 'INT'; + } + + /** + * @override + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'BIGSERIAL'; + } + return 'BIGINT'; + } + + /** + * @override + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT'; + } + + /** + * @override + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITHOUT TIME ZONE'; + } + + /** + * @override + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITH TIME ZONE'; + } + + /** + * @override + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * @override + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME(0) WITHOUT TIME ZONE'; + } + + /** + * @override + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return ''; + } + + /** + * Gets the SQL snippet used to declare a VARCHAR column on the MySql platform. + * + * @params array $field + * @override + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** @override */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * Get the platform name for this instance + * + * @return string + */ + public function getName() + { + return 'postgresql'; + } + + /** + * Gets the character casing of a column in an SQL result set. + * + * PostgreSQL returns all column names in SQL result sets in lowercase. + * + * @param string $column The column name for which to get the correct character casing. + * @return string The column name in the character casing used in SQL result sets. + */ + public function getSQLResultCasing($column) + { + return strtolower($column); + } + + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:sO'; + } + + /** + * Get the insert sql for an empty insert statement + * + * @param string $tableName + * @param string $identifierColumnName + * @return string $sql + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)'; + } + + /** + * @inheritdoc + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE '.$tableName.' '.(($cascade)?'CASCADE':''); + } + + public function getReadLockSQL() + { + return 'FOR SHARE'; + } + + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'smallint' => 'smallint', + 'int2' => 'smallint', + 'serial' => 'integer', + 'serial4' => 'integer', + 'int' => 'integer', + 'int4' => 'integer', + 'integer' => 'integer', + 'bigserial' => 'bigint', + 'serial8' => 'bigint', + 'bigint' => 'bigint', + 'int8' => 'bigint', + 'bool' => 'boolean', + 'boolean' => 'boolean', + 'text' => 'text', + 'varchar' => 'string', + 'interval' => 'string', + '_varchar' => 'string', + 'char' => 'string', + 'bpchar' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'timestamptz' => 'datetimetz', + 'time' => 'time', + 'timetz' => 'time', + 'float' => 'float', + 'float4' => 'float', + 'float8' => 'float', + 'double' => 'float', + 'double precision' => 'float', + 'real' => 'float', + 'decimal' => 'decimal', + 'money' => 'decimal', + 'numeric' => 'decimal', + 'year' => 'date', + 'bytea' => 'blob', + ); + } + + public function getVarcharMaxLength() + { + return 65535; + } + + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\PostgreSQLKeywords'; + } + + /** + * Gets the SQL Snippet used to declare a BLOB column type. + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BYTEA'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php new file mode 100644 index 0000000..2bbcee2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Platform to ensure compatibility of Doctrine with SQLServer2005 version and + * higher. + * + * Differences to SQL Server 2008 are: + * + * - DATETIME2 datatype does not exist, only DATETIME which has a precision of + * 3. This is not supported by PHP DateTime, so we are emulating it by + * setting .000 manually. + * - Starting with SQLServer2005 VARCHAR(MAX), VARBINARY(MAX) and + * NVARCHAR(max) replace the old TEXT, NTEXT and IMAGE types. See + * {@link http://www.sql-server-helper.com/faq/sql-server-2005-varchar-max-p01.aspx} + * for more information. + */ +class SQLServer2005Platform extends SQLServerPlatform +{ + /** + * @override + */ + public function supportsLimitOffset() + { + return true; + } + + /** @override */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'VARCHAR(MAX)'; + } +} + diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php new file mode 100644 index 0000000..2938d4a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Platform to ensure compatibility of Doctrine with SQLServer2008 version. + * + * Differences to SQL Server 2005 and before are that a new DATETIME2 type was + * introduced that has a higher precision. + */ +class SQLServer2008Platform extends SQLServer2005Platform +{ + /** + * @override + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + // 3 - microseconds precision length + // http://msdn.microsoft.com/en-us/library/ms187819.aspx + return 'DATETIME2(6)'; + } + + /** + * @override + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * @override + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME(0)'; + } + + /** + * @override + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.u'; + } + + /** + * @override + */ + public function getDateFormatString() + { + return 'Y-m-d'; + } + + /** + * @override + */ + public function getTimeFormatString() + { + return 'H:i:s'; + } + + /** + * Adding Datetime2 Type + */ + protected function initializeDoctrineTypeMappings() + { + parent::initializeDoctrineTypeMappings(); + $this->doctrineTypeMapping['datetime2'] = 'datetime'; + $this->doctrineTypeMapping['date'] = 'date'; + $this->doctrineTypeMapping['time'] = 'time'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php new file mode 100644 index 0000000..ddb075a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php @@ -0,0 +1,865 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\Index, + Doctrine\DBAL\Schema\Table; + +/** + * The SQLServerPlatform provides the behavior, features and SQL dialect of the + * Microsoft SQL Server database platform. + * + * @since 2.0 + * @author Roman Borschel + * @author Jonathan H. Wage + * @author Benjamin Eberlei + */ +class SQLServerPlatform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')'; + } + + public function getDateAddDaysExpression($date, $days) + { + return 'DATEADD(day, ' . $days . ', ' . $date . ')'; + } + + public function getDateSubDaysExpression($date, $days) + { + return 'DATEADD(day, -1 * ' . $days . ', ' . $date . ')'; + } + + public function getDateAddMonthExpression($date, $months) + { + return 'DATEADD(month, ' . $months . ', ' . $date . ')'; + } + + public function getDateSubMonthExpression($date, $months) + { + return 'DATEADD(month, -1 * ' . $months . ', ' . $date . ')'; + } + + /** + * Whether the platform prefers identity columns for ID generation. + * MsSql prefers "autoincrement" identity columns since sequences can only + * be emulated with a table. + * + * @return boolean + * @override + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * Whether the platform supports identity columns. + * MsSql supports this through AUTO_INCREMENT columns. + * + * @return boolean + * @override + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * Whether the platform supports releasing savepoints. + * + * @return boolean + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * create a new database + * + * @param string $name name of the database that should be created + * @return string + * @override + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * drop an existing database + * + * @param string $name name of the database that should be dropped + * @return string + * @override + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * @override + */ + public function supportsCreateDropDatabase() + { + return false; + } + + /** + * @override + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if ($foreignKey instanceof \Doctrine\DBAL\Schema\ForeignKeyConstraint) { + $foreignKey = $foreignKey->getQuotedName($this); + } + + if ($table instanceof \Doctrine\DBAL\Schema\Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; + } + + /** + * @override + */ + public function getDropIndexSQL($index, $table=null) + { + if ($index instanceof \Doctrine\DBAL\Schema\Index) { + $index_ = $index; + $index = $index->getQuotedName($this); + } else if (!is_string($index)) { + throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + if (!isset($table)) { + return 'DROP INDEX ' . $index; + } else { + if ($table instanceof \Doctrine\DBAL\Schema\Table) { + $table = $table->getQuotedName($this); + } + + return "IF EXISTS (SELECT * FROM sysobjects WHERE name = '$index') + ALTER TABLE " . $table . " DROP CONSTRAINT " . $index . " + ELSE + DROP INDEX " . $index . " ON " . $table; + } + } + + /** + * @override + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + // @todo does other code breaks because of this? + // foce primary keys to be not null + foreach ($columns as &$column) { + if (isset($column['primary']) && $column['primary']) { + $column['notnull'] = true; + } + } + + $columnListSql = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && !empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $name => $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if (isset($options['primary']) && !empty($options['primary'])) { + $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')'; + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql; + + $check = $this->getCheckDeclarationSQL($columns); + if (!empty($check)) { + $query .= ', ' . $check; + } + $query .= ')'; + + $sql[] = $query; + + if (isset($options['indexes']) && !empty($options['indexes'])) { + foreach ($options['indexes'] AS $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + } + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] AS $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * @override + */ + public function getUniqueConstraintDeclarationSQL($name, Index $index) + { + $constraint = parent::getUniqueConstraintDeclarationSQL($name, $index); + + $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); + + return $constraint; + } + + /** + * @override + */ + public function getCreateIndexSQL(Index $index, $table) + { + $constraint = parent::getCreateIndexSQL($index, $table); + + if ($index->isUnique()) { + $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); + } + + return $constraint; + } + + /** + * Extend unique key constraint with required filters + * + * @param string $sql + * @param Index $index + * @return string + */ + private function _appendUniqueConstraintDefinition($sql, Index $index) + { + $fields = array(); + foreach ($index->getColumns() as $field => $definition) { + if (!is_array($definition)) { + $field = $definition; + } + + $fields[] = $field . ' IS NOT NULL'; + } + + return $sql . ' WHERE ' . implode(' AND ', $fields); + } + + /** + * @override + */ + public function getAlterTableSQL(TableDiff $diff) + { + $queryParts = array(); + $sql = array(); + $columnSql = array(); + + if ($diff->newName !== false) { + $queryParts[] = 'RENAME TO ' . $diff->newName; + } + + foreach ($diff->addedColumns AS $fieldName => $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + foreach ($diff->removedColumns AS $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); + } + + foreach ($diff->changedColumns AS $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + /* @var $columnDiff Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + $queryParts[] = 'ALTER COLUMN ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + foreach ($diff->renamedColumns AS $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $sql[] = "sp_RENAME '". $diff->name. ".". $oldColumnName . "' , '".$column->getQuotedName($this)."', 'COLUMN'"; + $queryParts[] = 'ALTER COLUMN ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + $tableSql = array(); + + if ($this->onSchemaAlterTable($diff, $tableSql)) { + return array_merge($tableSql, $columnSql); + } + + foreach ($queryParts as $query) { + $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query; + } + + $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff)); + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * @override + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES'; + } + + /** + * @override + */ + public function getShowDatabasesSQL() + { + return 'SHOW DATABASES'; + } + + /** + * @override + */ + public function getListTablesSQL() + { + // "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams + return "SELECT name FROM sysobjects WHERE type = 'U' AND name != 'sysdiagrams' ORDER BY name"; + } + + /** + * @override + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "exec sp_columns @table_name = '" . $table . "'"; + } + + /** + * @override + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + return "SELECT f.name AS ForeignKey, + SCHEMA_NAME (f.SCHEMA_ID) AS SchemaName, + OBJECT_NAME (f.parent_object_id) AS TableName, + COL_NAME (fc.parent_object_id,fc.parent_column_id) AS ColumnName, + SCHEMA_NAME (o.SCHEMA_ID) ReferenceSchemaName, + OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName, + COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS ReferenceColumnName, + f.delete_referential_action_desc, + f.update_referential_action_desc + FROM sys.foreign_keys AS f + INNER JOIN sys.foreign_key_columns AS fc + INNER JOIN sys.objects AS o ON o.OBJECT_ID = fc.referenced_object_id + ON f.OBJECT_ID = fc.constraint_object_id + WHERE OBJECT_NAME (f.parent_object_id) = '" . $table . "'"; + } + + /** + * @override + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "exec sp_helpindex '" . $table . "'"; + } + + /** + * @override + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * @override + */ + public function getListViewsSQL($database) + { + return "SELECT name FROM sysobjects WHERE type = 'V' ORDER BY name"; + } + + /** + * @override + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW ' . $name; + } + + /** + * Returns the regular expression operator. + * + * @return string + * @override + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } + + /** + * Returns global unique identifier + * + * @return string to get global unique identifier + * @override + */ + public function getGuidExpression() + { + return 'UUID()'; + } + + /** + * @override + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'CHARINDEX(' . $substr . ', ' . $str . ')'; + } else { + return 'CHARINDEX(' . $substr . ', ' . $str . ', ' . $startPos . ')'; + } + } + + /** + * @override + */ + public function getModExpression($expression1, $expression2) + { + return $expression1 . ' % ' . $expression2; + } + + /** + * @override + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + $trimFn = ''; + + if (!$char) { + if ($pos == self::TRIM_LEADING) { + $trimFn = 'LTRIM'; + } else if ($pos == self::TRIM_TRAILING) { + $trimFn = 'RTRIM'; + } else { + return 'LTRIM(RTRIM(' . $str . '))'; + } + + return $trimFn . '(' . $str . ')'; + } else { + /** Original query used to get those expressions + declare @c varchar(100) = 'xxxBarxxx', @trim_char char(1) = 'x'; + declare @pat varchar(10) = '%[^' + @trim_char + ']%'; + select @c as string + , @trim_char as trim_char + , stuff(@c, 1, patindex(@pat, @c) - 1, null) as trim_leading + , reverse(stuff(reverse(@c), 1, patindex(@pat, reverse(@c)) - 1, null)) as trim_trailing + , reverse(stuff(reverse(stuff(@c, 1, patindex(@pat, @c) - 1, null)), 1, patindex(@pat, reverse(stuff(@c, 1, patindex(@pat, @c) - 1, null))) - 1, null)) as trim_both; + */ + $pattern = "'%[^' + $char + ']%'"; + + if ($pos == self::TRIM_LEADING) { + return 'stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)'; + } else if ($pos == self::TRIM_TRAILING) { + return 'reverse(stuff(reverse(' . $str . '), 1, patindex(' . $pattern . ', reverse(' . $str . ')) - 1, null))'; + } else { + return 'reverse(stuff(reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)), 1, patindex(' . $pattern . ', reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null))) - 1, null))'; + } + } + } + + /** + * @override + */ + public function getConcatExpression() + { + $args = func_get_args(); + return '(' . implode(' + ', $args) . ')'; + } + + public function getListDatabasesSQL() + { + return 'SELECT * FROM SYS.DATABASES'; + } + + /** + * @override + */ + public function getSubstringExpression($value, $from, $len = null) + { + if (!is_null($len)) { + return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $len . ')'; + } + return 'SUBSTRING(' . $value . ', ' . $from . ', LEN(' . $value . ') - ' . $from . ' + 1)'; + } + + /** + * @override + */ + public function getLengthExpression($column) + { + return 'LEN(' . $column . ')'; + } + + /** + * @override + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * @override + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * @override + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * @override + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** @override */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'NCHAR(' . $length . ')' : 'CHAR(255)') : ($length ? 'NVARCHAR(' . $length . ')' : 'NVARCHAR(255)'); + } + + /** @override */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * @override + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if (!empty($columnDef['autoincrement'])) { + $autoinc = ' IDENTITY'; + } + $unsigned = (isset($columnDef['unsigned']) && $columnDef['unsigned']) ? ' UNSIGNED' : ''; + + return $unsigned . $autoinc; + } + + /** + * @override + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * @override + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * @override + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * @override + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BIT'; + } + + /** + * Adds an adapter-specific LIMIT clause to the SELECT statement. + * + * @param string $query + * @param integer $limit + * @param integer $offset + * @link http://lists.bestpractical.com/pipermail/rt-devel/2005-June/007339.html + * @return string + */ + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + if ($limit > 0) { + if ($offset == 0) { + $query = preg_replace('/^(SELECT\s(DISTINCT\s)?)/i', '\1TOP ' . $limit . ' ', $query); + } else { + $orderby = stristr($query, 'ORDER BY'); + + if (!$orderby) { + $over = 'ORDER BY (SELECT 0)'; + } else { + $over = preg_replace('/\"[^,]*\".\"([^,]*)\"/i', '"inner_tbl"."$1"', $orderby); + } + + // Remove ORDER BY clause from $query + $query = preg_replace('/\s+ORDER BY(.*)/', '', $query); + $query = preg_replace('/^SELECT\s/', '', $query); + + $start = $offset + 1; + $end = $offset + $limit; + + $query = "SELECT * FROM (SELECT ROW_NUMBER() OVER ($over) AS \"doctrine_rownum\", $query) AS doctrine_tbl WHERE \"doctrine_rownum\" BETWEEN $start AND $end"; + } + } + + return $query; + } + + /** + * @override + */ + public function supportsLimitOffset() + { + return false; + } + + /** + * @override + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + if (is_bool($value) || is_numeric($item)) { + $item[$key] = ($value) ? 1 : 0; + } + } + } else { + if (is_bool($item) || is_numeric($item)) { + $item = ($item) ? 1 : 0; + } + } + return $item; + } + + /** + * @override + */ + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE TABLE"; + } + + /** + * @override + */ + public function getTemporaryTableName($tableName) + { + return '#' . $tableName; + } + + /** + * @override + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * @override + */ + public function getDateFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * @override + */ + public function getTimeFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * @override + */ + public function getDateTimeTzFormatString() + { + return $this->getDateTimeFormatString(); + } + + /** + * Get the platform name for this instance + * + * @return string + */ + public function getName() + { + return 'mssql'; + } + + /** + * @override + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'bigint' => 'bigint', + 'numeric' => 'decimal', + 'bit' => 'boolean', + 'smallint' => 'smallint', + 'decimal' => 'decimal', + 'smallmoney' => 'integer', + 'int' => 'integer', + 'tinyint' => 'smallint', + 'money' => 'integer', + 'float' => 'float', + 'real' => 'float', + 'double' => 'float', + 'double precision' => 'float', + 'datetimeoffset' => 'datetimetz', + 'smalldatetime' => 'datetime', + 'datetime' => 'datetime', + 'char' => 'string', + 'varchar' => 'string', + 'text' => 'text', + 'nchar' => 'string', + 'nvarchar' => 'string', + 'ntext' => 'text', + 'binary' => 'text', + 'varbinary' => 'blob', + 'image' => 'text', + ); + } + + /** + * Generate SQL to create a new savepoint + * + * @param string $savepoint + * @return string + */ + public function createSavePoint($savepoint) + { + return 'SAVE TRANSACTION ' . $savepoint; + } + + /** + * Generate SQL to release a savepoint + * + * @param string $savepoint + * @return string + */ + public function releaseSavePoint($savepoint) + { + return ''; + } + + /** + * Generate SQL to rollback a savepoint + * + * @param string $savepoint + * @return string + */ + public function rollbackSavePoint($savepoint) + { + return 'ROLLBACK TRANSACTION ' . $savepoint; + } + + /** + * @override + */ + public function appendLockHint($fromClause, $lockMode) + { + // @todo coorect + if ($lockMode == \Doctrine\DBAL\LockMode::PESSIMISTIC_READ) { + return $fromClause . ' WITH (tablockx)'; + } else if ($lockMode == \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE) { + return $fromClause . ' WITH (tablockx)'; + } else { + return $fromClause; + } + } + + /** + * @override + */ + public function getForUpdateSQL() + { + return ' '; + } + + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\MsSQLKeywords'; + } + + /** + * {@inheritDoc} + */ + public function quoteSingleIdentifier($str) + { + return "[" . str_replace("]", "][", $str) . "]"; + } + + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE TABLE '.$tableName; + } + + /** + * Gets the SQL Snippet used to declare a BLOB column type. + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'VARBINARY(MAX)'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php new file mode 100644 index 0000000..9cfa726 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php @@ -0,0 +1,519 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; + +/** + * The SqlitePlatform class describes the specifics and dialects of the SQLite + * database platform. + * + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + * @todo Rename: SQLitePlatform + */ +class SqlitePlatform extends AbstractPlatform +{ + /** + * returns the regular expression operator + * + * @return string + * @override + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } + + /** + * Return string to call a variable with the current timestamp inside an SQL statement + * There are three special variables for current date and time. + * + * @return string sqlite function as string + * @override + */ + public function getNowExpression($type = 'timestamp') + { + switch ($type) { + case 'time': + return 'time(\'now\')'; + case 'date': + return 'date(\'now\')'; + case 'timestamp': + default: + return 'datetime(\'now\')'; + } + } + + /** + * Trim a string, leading/trailing/both and with a given char which defaults to space. + * + * @param string $str + * @param int $pos + * @param string $char + * @return string + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + $trimFn = ''; + $trimChar = ($char != false) ? (', ' . $char) : ''; + + if ($pos == self::TRIM_LEADING) { + $trimFn = 'LTRIM'; + } else if($pos == self::TRIM_TRAILING) { + $trimFn = 'RTRIM'; + } else { + $trimFn = 'TRIM'; + } + + return $trimFn . '(' . $str . $trimChar . ')'; + } + + /** + * return string to call a function to get a substring inside an SQL statement + * + * Note: Not SQL92, but common functionality. + * + * SQLite only supports the 2 parameter variant of this function + * + * @param string $value an sql string literal or column name/alias + * @param integer $position where to start the substring portion + * @param integer $length the substring portion length + * @return string SQL substring function with given parameters + * @override + */ + public function getSubstringExpression($value, $position, $length = null) + { + if ($length !== null) { + return 'SUBSTR(' . $value . ', ' . $position . ', ' . $length . ')'; + } + return 'SUBSTR(' . $value . ', ' . $position . ', LENGTH(' . $value . '))'; + } + + /** + * returns the position of the first occurrence of substring $substr in string $str + * + * @param string $substr literal string to find + * @param string $str literal string + * @param int $pos position to start at, beginning of string by default + * @return integer + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE('.$str.', '.$substr.')'; + } else { + return 'LOCATE('.$str.', '.$substr.', '.$startPos.')'; + } + } + + public function getDateDiffExpression($date1, $date2) + { + return 'ROUND(JULIANDAY('.$date1 . ')-JULIANDAY('.$date2.'))'; + } + + public function getDateAddDaysExpression($date, $days) + { + return "DATE(" . $date . ",'+". $days . " day')"; + } + + public function getDateSubDaysExpression($date, $days) + { + return "DATE(" . $date . ",'-". $days . " day')"; + } + + public function getDateAddMonthExpression($date, $months) + { + return "DATE(" . $date . ",'+". $months . " month')"; + } + + public function getDateSubMonthExpression($date, $months) + { + return "DATE(" . $date . ",'-". $months . " month')"; + } + + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case \Doctrine\DBAL\Connection::TRANSACTION_READ_UNCOMMITTED: + return 0; + case \Doctrine\DBAL\Connection::TRANSACTION_READ_COMMITTED: + case \Doctrine\DBAL\Connection::TRANSACTION_REPEATABLE_READ: + case \Doctrine\DBAL\Connection::TRANSACTION_SERIALIZABLE: + return 1; + default: + return parent::_getTransactionIsolationLevelSQL($level); + } + } + + public function getSetTransactionIsolationSQL($level) + { + return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * @override + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * @override + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BOOLEAN'; + } + + /** + * @override + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * @override + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * @override + */ + public function getTinyIntTypeDeclarationSql(array $field) + { + return $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * @override + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * @override + */ + public function getMediumIntTypeDeclarationSql(array $field) + { + return $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * @override + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * @override + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * @override + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * @override + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return 'INTEGER'; + } + + /** + * create a new table + * + * @param string $name Name of the database that should be created + * @param array $fields Associative array that contains the definition of each field of the new table + * The indexes of the array entries are the names of the fields of the table an + * the array entry values are associative arrays like those that are meant to be + * passed with the field definitions to get[Type]Declaration() functions. + * array( + * 'id' => array( + * 'type' => 'integer', + * 'unsigned' => 1 + * 'notnull' => 1 + * 'default' => 0 + * ), + * 'name' => array( + * 'type' => 'text', + * 'length' => 12 + * ), + * 'password' => array( + * 'type' => 'text', + * 'length' => 12 + * ) + * ); + * @param array $options An associative array of table options: + * + * @return void + * @override + */ + protected function _getCreateTableSQL($name, array $columns, array $options = array()) + { + $name = str_replace(".", "__", $name); + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $keyColumns = array_map(array($this, 'quoteIdentifier'), $keyColumns); + $queryFields.= ', PRIMARY KEY('.implode(', ', $keyColumns).')'; + } + + $query[] = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')'; + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index => $indexDef) { + $query[] = $this->getCreateIndexSQL($indexDef, $name); + } + } + if (isset($options['unique']) && ! empty($options['unique'])) { + foreach ($options['unique'] as $index => $indexDef) { + $query[] = $this->getCreateIndexSQL($indexDef, $name); + } + } + return $query; + } + + /** + * {@inheritdoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT'); + } + + public function getClobTypeDeclarationSQL(array $field) + { + return 'CLOB'; + } + + public function getListTableConstraintsSQL($table) + { + $table = str_replace(".", "__", $table); + return "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = '$table' AND sql NOT NULL ORDER BY name"; + } + + public function getListTableColumnsSQL($table, $currentDatabase = null) + { + $table = str_replace(".", "__", $table); + return "PRAGMA table_info($table)"; + } + + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $table = str_replace(".", "__", $table); + return "PRAGMA index_list($table)"; + } + + public function getListTablesSQL() + { + return "SELECT name FROM sqlite_master WHERE type = 'table' AND name != 'sqlite_sequence' AND name != 'geometry_columns' AND name != 'spatial_ref_sys' " + . "UNION ALL SELECT name FROM sqlite_temp_master " + . "WHERE type = 'table' ORDER BY name"; + } + + public function getListViewsSQL($database) + { + return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL"; + } + + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * SQLite does support foreign key constraints, but only in CREATE TABLE statements... + * This really limits their usefulness and requires SQLite specific handling, so + * we simply say that SQLite does NOT support foreign keys for now... + * + * @return boolean FALSE + * @override + */ + public function supportsForeignKeyConstraints() + { + return false; + } + + public function supportsAlterTable() + { + return false; + } + + public function supportsIdentityColumns() + { + return true; + } + + /** + * Get the platform name for this instance + * + * @return string + */ + public function getName() + { + return 'sqlite'; + } + + /** + * @inheritdoc + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + $tableName = str_replace(".", "__", $tableName); + return 'DELETE FROM '.$tableName; + } + + /** + * User-defined function for Sqlite that is used with PDO::sqliteCreateFunction() + * + * @param int|float $value + * @return float + */ + static public function udfSqrt($value) + { + return sqrt($value); + } + + /** + * User-defined function for Sqlite that implements MOD(a, b) + */ + static public function udfMod($a, $b) + { + return ($a % $b); + } + + /** + * @param string $str + * @param string $substr + * @param int $offset + */ + static public function udfLocate($str, $substr, $offset = 0) + { + $pos = strpos($str, $substr, $offset); + if ($pos !== false) { + return $pos+1; + } + return 0; + } + + public function getForUpdateSql() + { + return ''; + } + + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'boolean' => 'boolean', + 'tinyint' => 'boolean', + 'smallint' => 'smallint', + 'mediumint' => 'integer', + 'int' => 'integer', + 'integer' => 'integer', + 'serial' => 'integer', + 'bigint' => 'bigint', + 'bigserial' => 'bigint', + 'clob' => 'text', + 'tinytext' => 'text', + 'mediumtext' => 'text', + 'longtext' => 'text', + 'text' => 'text', + 'varchar' => 'string', + 'longvarchar' => 'string', + 'varchar2' => 'string', + 'nvarchar' => 'string', + 'image' => 'string', + 'ntext' => 'string', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'time' => 'time', + 'float' => 'float', + 'double' => 'float', + 'double precision' => 'float', + 'real' => 'float', + 'decimal' => 'decimal', + 'numeric' => 'decimal', + 'blob' => 'blob', + ); + } + + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords'; + } + + /** + * Gets the SQL Snippet used to declare a BLOB column type. + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BLOB'; + } + + public function getTemporaryTableName($tableName) + { + $tableName = str_replace(".", "__", $tableName); + return $tableName; + } + + /** + * Sqlite Platform emulates schema by underscoring each dot and generating tables + * into the default database. + * + * This hack is implemented to be able to use SQLite as testdriver when + * using schema supporting databases. + * + * @return bool + */ + public function canEmulateSchemas() + { + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php new file mode 100644 index 0000000..93ee9fc --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php @@ -0,0 +1,113 @@ +. + */ + + +namespace Doctrine\DBAL\Portability; + +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Cache\QueryCacheProfile; + +class Connection extends \Doctrine\DBAL\Connection +{ + const PORTABILITY_ALL = 255; + const PORTABILITY_NONE = 0; + const PORTABILITY_RTRIM = 1; + const PORTABILITY_EMPTY_TO_NULL = 4; + const PORTABILITY_FIX_CASE = 8; + + const PORTABILITY_ORACLE = 9; + const PORTABILITY_POSTGRESQL = 13; + const PORTABILITY_SQLITE = 13; + const PORTABILITY_OTHERVENDORS = 12; + + /** + * @var int + */ + private $portability = self::PORTABILITY_NONE; + + /** + * @var int + */ + private $case; + + public function connect() + { + $ret = parent::connect(); + if ($ret) { + $params = $this->getParams(); + if (isset($params['portability'])) { + if ($this->_platform->getName() === "oracle") { + $params['portability'] = $params['portability'] & self::PORTABILITY_ORACLE; + } else if ($this->_platform->getName() === "postgresql") { + $params['portability'] = $params['portability'] & self::PORTABILITY_POSTGRESQL; + } else if ($this->_platform->getName() === "sqlite") { + $params['portability'] = $params['portability'] & self::PORTABILITY_SQLITE; + } else { + $params['portability'] = $params['portability'] & self::PORTABILITY_OTHERVENDORS; + } + $this->portability = $params['portability']; + } + if (isset($params['fetch_case']) && $this->portability & self::PORTABILITY_FIX_CASE) { + if ($this->_conn instanceof \Doctrine\DBAL\Driver\PDOConnection) { + // make use of c-level support for case handling + $this->_conn->setAttribute(\PDO::ATTR_CASE, $params['fetch_case']); + } else { + $this->case = ($params['fetch_case'] == \PDO::CASE_LOWER) ? CASE_LOWER : CASE_UPPER; + } + } + } + return $ret; + } + + public function getPortability() + { + return $this->portability; + } + + public function getFetchCase() + { + return $this->case; + } + + public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null) + { + return new Statement(parent::executeQuery($query, $params, $types, $qcp), $this); + } + + /** + * Prepares an SQL statement. + * + * @param string $statement The SQL statement to prepare. + * @return Doctrine\DBAL\Driver\Statement The prepared statement. + */ + public function prepare($statement) + { + return new Statement(parent::prepare($statement), $this); + } + + public function query() + { + $this->connect(); + + $stmt = call_user_func_array(array($this->_conn, 'query'), func_get_args()); + return new Statement($stmt, $this); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php new file mode 100644 index 0000000..70622f6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php @@ -0,0 +1,191 @@ +. + */ + +namespace Doctrine\DBAL\Portability; + +use PDO; + +/** + * Portability Wrapper for a Statement + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class Statement implements \IteratorAggregate, \Doctrine\DBAL\Driver\Statement +{ + + /** + * @var int + */ + private $portability; + + /** + * @var Doctrine\DBAL\Driver\Statement + */ + private $stmt; + + /** + * @var int + */ + private $case; + + /** + * @var int + */ + private $defaultFetchStyle = PDO::FETCH_BOTH; + + /** + * Wraps Statement and applies portability measures + * + * @param Doctrine\DBAL\Driver\Statement $stmt + * @param Doctrine\DBAL\Connection $conn + */ + public function __construct($stmt, Connection $conn) + { + $this->stmt = $stmt; + $this->portability = $conn->getPortability(); + $this->case = $conn->getFetchCase(); + } + + public function bindParam($column, &$variable, $type = null) + { + return $this->stmt->bindParam($column, $variable, $type); + } + + public function bindValue($param, $value, $type = null) + { + return $this->stmt->bindValue($param, $value, $type); + } + + public function closeCursor() + { + return $this->stmt->closeCursor(); + } + + public function columnCount() + { + return $this->stmt->columnCount(); + } + + public function errorCode() + { + return $this->stmt->errorCode(); + } + + public function errorInfo() + { + return $this->stmt->errorInfo(); + } + + public function execute($params = null) + { + return $this->stmt->execute($params); + } + + public function setFetchMode($fetchStyle, $arg1 = null, $arg2 = null) + { + $this->defaultFetchStyle = $fetchStyle; + $this->stmt->setFetchMode($fetchStyle, $arg1, $arg2); + } + + public function getIterator() + { + $data = $this->fetchAll($this->defaultFetchStyle); + return new \ArrayIterator($data); + } + + public function fetch($fetchStyle = PDO::FETCH_BOTH) + { + $row = $this->stmt->fetch($fetchStyle); + + $row = $this->fixRow($row, + $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM), + !is_null($this->case) && ($fetchStyle == PDO::FETCH_ASSOC || $fetchStyle == PDO::FETCH_BOTH) && ($this->portability & Connection::PORTABILITY_FIX_CASE) + ); + + return $row; + } + + public function fetchAll($fetchStyle = PDO::FETCH_BOTH, $columnIndex = 0) + { + if ($columnIndex != 0) { + $rows = $this->stmt->fetchAll($fetchStyle, $columnIndex); + } else { + $rows = $this->stmt->fetchAll($fetchStyle); + } + + $iterateRow = $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM); + $fixCase = !is_null($this->case) && ($fetchStyle == PDO::FETCH_ASSOC || $fetchStyle == PDO::FETCH_BOTH) && ($this->portability & Connection::PORTABILITY_FIX_CASE); + if (!$iterateRow && !$fixCase) { + return $rows; + } + + foreach ($rows AS $num => $row) { + $rows[$num] = $this->fixRow($row, $iterateRow, $fixCase); + } + + return $rows; + } + + protected function fixRow($row, $iterateRow, $fixCase) + { + if (!$row) { + return $row; + } + + if ($fixCase) { + $row = array_change_key_case($row, $this->case); + } + + if ($iterateRow) { + foreach ($row AS $k => $v) { + if (($this->portability & Connection::PORTABILITY_EMPTY_TO_NULL) && $v === '') { + $row[$k] = null; + } else if (($this->portability & Connection::PORTABILITY_RTRIM) && is_string($v)) { + $row[$k] = rtrim($v); + } + } + } + return $row; + } + + public function fetchColumn($columnIndex = 0) + { + $value = $this->stmt->fetchColumn($columnIndex); + + if ($this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM)) { + if (($this->portability & Connection::PORTABILITY_EMPTY_TO_NULL) && $value === '') { + $value = null; + } else if (($this->portability & Connection::PORTABILITY_RTRIM) && is_string($value)) { + $value = rtrim($value); + } + } + + return $value; + } + + public function rowCount() + { + return $this->stmt->rowCount(); + } + +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php new file mode 100644 index 0000000..5da1889 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php @@ -0,0 +1,130 @@ +. + */ + +namespace Doctrine\DBAL\Query\Expression; + +/** + * Composite expression is responsible to build a group of similar expression. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class CompositeExpression implements \Countable +{ + /** + * Constant that represents an AND composite expression + */ + const TYPE_AND = 'AND'; + + /** + * Constant that represents an OR composite expression + */ + const TYPE_OR = 'OR'; + + /** + * @var string Holds the instance type of composite expression + */ + private $type; + + /** + * @var array Each expression part of the composite expression + */ + private $parts = array(); + + /** + * Constructor. + * + * @param string $type Instance type of composite expression + * @param array $parts Composition of expressions to be joined on composite expression + */ + public function __construct($type, array $parts = array()) + { + $this->type = $type; + + $this->addMultiple($parts); + } + + /** + * Adds multiple parts to composite expression. + * + * @param array $args + * + * @return CompositeExpression + */ + public function addMultiple(array $parts = array()) + { + foreach ((array) $parts as $part) { + $this->add($part); + } + + return $this; + } + + /** + * Adds an expression to composite expression. + * + * @param mixed $part + * @return CompositeExpression + */ + public function add($part) + { + if ( ! empty($part) || ($part instanceof self && $part->count() > 0)) { + $this->parts[] = $part; + } + + return $this; + } + + /** + * Retrieves the amount of expressions on composite expression. + * + * @return integer + */ + public function count() + { + return count($this->parts); + } + + /** + * Retrieve the string representation of this composite expression. + * + * @return string + */ + public function __toString() + { + if (count($this->parts) === 1) { + return (string) $this->parts[0]; + } + + return '(' . implode(') ' . $this->type . ' (', $this->parts) . ')'; + } + + /** + * Return type of this composite expression (AND/OR) + * + * @return string + */ + public function getType() + { + return $this->type; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php new file mode 100644 index 0000000..35f0762 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php @@ -0,0 +1,264 @@ +. + */ + +namespace Doctrine\DBAL\Query\Expression; + +use Doctrine\DBAL\Connection; + +/** + * ExpressionBuilder class is responsible to dynamically create SQL query parts. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class ExpressionBuilder +{ + const EQ = '='; + const NEQ = '<>'; + const LT = '<'; + const LTE = '<='; + const GT = '>'; + const GTE = '>='; + + /** + * @var Doctrine\DBAL\Connection DBAL Connection + */ + private $connection = null; + + /** + * Initializes a new ExpressionBuilder. + * + * @param Doctrine\DBAL\Connection $connection DBAL Connection + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * Creates a conjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?) AND (u.role = ?) + * $expr->andX('u.type = ?', 'u.role = ?')); + * + * @param mixed $x Optional clause. Defaults = null, but requires + * at least one defined when converting to string. + * @return CompositeExpression + */ + public function andX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + /** + * Creates a disjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?) OR (u.role = ?) + * $qb->where($qb->expr()->orX('u.type = ?', 'u.role = ?')); + * + * @param mixed $x Optional clause. Defaults = null, but requires + * at least one defined when converting to string. + * @return CompositeExpression + */ + public function orX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); + } + + /** + * Creates a comparison expression. + * + * @param mixed $x Left expression + * @param string $operator One of the ExpressionBuikder::* constants. + * @param mixed $y Right expression + * @return string + */ + public function comparison($x, $operator, $y) + { + return $x . ' ' . $operator . ' ' . $y; + } + + /** + * Creates an equality comparison expression with the given arguments. + * + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a = . Example: + * + * [php] + * // u.id = ? + * $expr->eq('u.id', '?'); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return string + */ + public function eq($x, $y) + { + return $this->comparison($x, self::EQ, $y); + } + + /** + * Creates a non equality comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <> . Example: + * + * [php] + * // u.id <> 1 + * $q->where($q->expr()->neq('u.id', '1')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return string + */ + public function neq($x, $y) + { + return $this->comparison($x, self::NEQ, $y); + } + + /** + * Creates a lower-than comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a < . Example: + * + * [php] + * // u.id < ? + * $q->where($q->expr()->lt('u.id', '?')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return string + */ + public function lt($x, $y) + { + return $this->comparison($x, self::LT, $y); + } + + /** + * Creates a lower-than-equal comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <= . Example: + * + * [php] + * // u.id <= ? + * $q->where($q->expr()->lte('u.id', '?')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return string + */ + public function lte($x, $y) + { + return $this->comparison($x, self::LTE, $y); + } + + /** + * Creates a greater-than comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a > . Example: + * + * [php] + * // u.id > ? + * $q->where($q->expr()->gt('u.id', '?')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return string + */ + public function gt($x, $y) + { + return $this->comparison($x, self::GT, $y); + } + + /** + * Creates a greater-than-equal comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a >= . Example: + * + * [php] + * // u.id >= ? + * $q->where($q->expr()->gte('u.id', '?')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return string + */ + public function gte($x, $y) + { + return $this->comparison($x, self::GTE, $y); + } + + /** + * Creates an IS NULL expression with the given arguments. + * + * @param string $x Field in string format to be restricted by IS NULL + * + * @return string + */ + public function isNull($x) + { + return $x . ' IS NULL'; + } + + /** + * Creates an IS NOT NULL expression with the given arguments. + * + * @param string $x Field in string format to be restricted by IS NOT NULL + * + * @return string + */ + public function isNotNull($x) + { + return $x . ' IS NOT NULL'; + } + + /** + * Creates a LIKE() comparison expression with the given arguments. + * + * @param string $x Field in string format to be inspected by LIKE() comparison. + * @param mixed $y Argument to be used in LIKE() comparison. + * + * @return string + */ + public function like($x, $y) + { + return $this->comparison($x, 'LIKE', $y); + } + + /** + * Quotes a given input parameter. + * + * @param mixed $input Parameter to be quoted. + * @param string $type Type of the parameter. + * + * @return string + */ + public function literal($input, $type = null) + { + return $this->connection->quote($input, $type); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php new file mode 100644 index 0000000..4fb3a06 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php @@ -0,0 +1,1091 @@ +. + */ + +namespace Doctrine\DBAL\Query; + +use Doctrine\DBAL\Query\Expression\CompositeExpression, + Doctrine\DBAL\Connection; + +/** + * QueryBuilder class is responsible to dynamically create SQL queries. + * + * Important: Verify that every feature you use will work with your database vendor. + * SQL Query Builder does not attempt to validate the generated SQL at all. + * + * The query builder does no validation whatsoever if certain features even work with the + * underlying database vendor. Limit queries and joins are NOT applied to UPDATE and DELETE statements + * even if some vendors such as MySQL support it. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class QueryBuilder +{ + /* The query types. */ + const SELECT = 0; + const DELETE = 1; + const UPDATE = 2; + + /** The builder states. */ + const STATE_DIRTY = 0; + const STATE_CLEAN = 1; + + /** + * @var Doctrine\DBAL\Connection DBAL Connection + */ + private $connection = null; + + /** + * @var array The array of SQL parts collected. + */ + private $sqlParts = array( + 'select' => array(), + 'from' => array(), + 'join' => array(), + 'set' => array(), + 'where' => null, + 'groupBy' => array(), + 'having' => null, + 'orderBy' => array() + ); + + /** + * @var string The complete SQL string for this query. + */ + private $sql; + + /** + * @var array The query parameters. + */ + private $params = array(); + + /** + * @var array The parameter type map of this query. + */ + private $paramTypes = array(); + + /** + * @var integer The type of query this is. Can be select, update or delete. + */ + private $type = self::SELECT; + + /** + * @var integer The state of the query object. Can be dirty or clean. + */ + private $state = self::STATE_CLEAN; + + /** + * @var integer The index of the first result to retrieve. + */ + private $firstResult = null; + + /** + * @var integer The maximum number of results to retrieve. + */ + private $maxResults = null; + + /** + * The counter of bound parameters used with {@see bindValue) + * + * @var int + */ + private $boundCounter = 0; + + /** + * Initializes a new QueryBuilder. + * + * @param Doctrine\DBAL\Connection $connection DBAL Connection + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * Gets an ExpressionBuilder used for object-oriented construction of query expressions. + * This producer method is intended for convenient inline usage. Example: + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where($qb->expr()->eq('u.id', 1)); + * + * + * For more complex expression construction, consider storing the expression + * builder object in a local variable. + * + * @return Doctrine\DBAL\Query\ExpressionBuilder + */ + public function expr() + { + return $this->connection->getExpressionBuilder(); + } + + /** + * Get the type of the currently built query. + * + * @return integer + */ + public function getType() + { + return $this->type; + } + + /** + * Get the associated DBAL Connection for this query builder. + * + * @return Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Get the state of this query builder instance. + * + * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. + */ + public function getState() + { + return $this->state; + } + + /** + * Execute this query using the bound parameters and their types. + * + * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate} + * for insert, update and delete statements. + * + * @return mixed + */ + public function execute() + { + if ($this->type == self::SELECT) { + return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes); + } else { + return $this->connection->executeUpdate($this->getSQL(), $this->params, $this->paramTypes); + } + } + + /** + * Get the complete SQL string formed by the current specifications of this QueryBuilder. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * echo $qb->getSQL(); // SELECT u FROM User u + * + * + * @return string The sql query string. + */ + public function getSQL() + { + if ($this->sql !== null && $this->state === self::STATE_CLEAN) { + return $this->sql; + } + + $sql = ''; + + switch ($this->type) { + case self::DELETE: + $sql = $this->getSQLForDelete(); + break; + + case self::UPDATE: + $sql = $this->getSQLForUpdate(); + break; + + case self::SELECT: + default: + $sql = $this->getSQLForSelect(); + break; + } + + $this->state = self::STATE_CLEAN; + $this->sql = $sql; + + return $sql; + } + + /** + * Sets a query parameter for the query being constructed. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.id = :user_id') + * ->setParameter(':user_id', 1); + * + * + * @param string|integer $key The parameter position or name. + * @param mixed $value The parameter value. + * @param string|null $type PDO::PARAM_* + * @return QueryBuilder This QueryBuilder instance. + */ + public function setParameter($key, $value, $type = null) + { + if ($type !== null) { + $this->paramTypes[$key] = $type; + } + + $this->params[$key] = $value; + + return $this; + } + + /** + * Sets a collection of query parameters for the query being constructed. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.id = :user_id1 OR u.id = :user_id2') + * ->setParameters(array( + * ':user_id1' => 1, + * ':user_id2' => 2 + * )); + * + * + * @param array $params The query parameters to set. + * @return QueryBuilder This QueryBuilder instance. + */ + public function setParameters(array $params, array $types = array()) + { + $this->paramTypes = $types; + $this->params = $params; + + return $this; + } + + /** + * Gets all defined query parameters for the query being constructed. + * + * @return array The currently defined query parameters. + */ + public function getParameters() + { + return $this->params; + } + + /** + * Gets a (previously set) query parameter of the query being constructed. + * + * @param mixed $key The key (index or name) of the bound parameter. + * @return mixed The value of the bound parameter. + */ + public function getParameter($key) + { + return isset($this->params[$key]) ? $this->params[$key] : null; + } + + /** + * Sets the position of the first result to retrieve (the "offset"). + * + * @param integer $firstResult The first result to return. + * @return Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function setFirstResult($firstResult) + { + $this->state = self::STATE_DIRTY; + $this->firstResult = $firstResult; + return $this; + } + + /** + * Gets the position of the first result the query object was set to retrieve (the "offset"). + * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder. + * + * @return integer The position of the first result. + */ + public function getFirstResult() + { + return $this->firstResult; + } + + /** + * Sets the maximum number of results to retrieve (the "limit"). + * + * @param integer $maxResults The maximum number of results to retrieve. + * @return Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function setMaxResults($maxResults) + { + $this->state = self::STATE_DIRTY; + $this->maxResults = $maxResults; + return $this; + } + + /** + * Gets the maximum number of results the query object was set to retrieve (the "limit"). + * Returns NULL if {@link setMaxResults} was not applied to this query builder. + * + * @return integer Maximum number of results. + */ + public function getMaxResults() + { + return $this->maxResults; + } + + /** + * Either appends to or replaces a single, generic query part. + * + * The available parts are: 'select', 'from', 'set', 'where', + * 'groupBy', 'having' and 'orderBy'. + * + * @param string $sqlPartName + * @param string $sqlPart + * @param string $append + * @return Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function add($sqlPartName, $sqlPart, $append = false) + { + $isArray = is_array($sqlPart); + $isMultiple = is_array($this->sqlParts[$sqlPartName]); + + if ($isMultiple && !$isArray) { + $sqlPart = array($sqlPart); + } + + $this->state = self::STATE_DIRTY; + + if ($append) { + if ($sqlPartName == "orderBy" || $sqlPartName == "groupBy" || $sqlPartName == "select" || $sqlPartName == "set") { + foreach ($sqlPart AS $part) { + $this->sqlParts[$sqlPartName][] = $part; + } + } else if ($isArray && is_array($sqlPart[key($sqlPart)])) { + $key = key($sqlPart); + $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key]; + } else if ($isMultiple) { + $this->sqlParts[$sqlPartName][] = $sqlPart; + } else { + $this->sqlParts[$sqlPartName] = $sqlPart; + } + + return $this; + } + + $this->sqlParts[$sqlPartName] = $sqlPart; + + return $this; + } + + /** + * Specifies an item that is to be returned in the query result. + * Replaces any previously specified selections, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.id', 'p.id') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); + * + * + * @param mixed $select The selection expressions. + * @return QueryBuilder This QueryBuilder instance. + */ + public function select($select = null) + { + $this->type = self::SELECT; + + if (empty($select)) { + return $this; + } + + $selects = is_array($select) ? $select : func_get_args(); + + return $this->add('select', $selects, false); + } + + /** + * Adds an item that is to be returned in the query result. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.id') + * ->addSelect('p.id') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id'); + * + * + * @param mixed $select The selection expression. + * @return QueryBuilder This QueryBuilder instance. + */ + public function addSelect($select = null) + { + $this->type = self::SELECT; + + if (empty($select)) { + return $this; + } + + $selects = is_array($select) ? $select : func_get_args(); + + return $this->add('select', $selects, true); + } + + /** + * Turns the query being built into a bulk delete query that ranges over + * a certain table. + * + * + * $qb = $conn->createQueryBuilder() + * ->delete('users', 'u') + * ->where('u.id = :user_id'); + * ->setParameter(':user_id', 1); + * + * + * @param string $delete The table whose rows are subject to the deletion. + * @param string $alias The table alias used in the constructed query. + * @return QueryBuilder This QueryBuilder instance. + */ + public function delete($delete = null, $alias = null) + { + $this->type = self::DELETE; + + if ( ! $delete) { + return $this; + } + + return $this->add('from', array( + 'table' => $delete, + 'alias' => $alias + )); + } + + /** + * Turns the query being built into a bulk update query that ranges over + * a certain table + * + * + * $qb = $conn->createQueryBuilder() + * ->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $update The table whose rows are subject to the update. + * @param string $alias The table alias used in the constructed query. + * @return QueryBuilder This QueryBuilder instance. + */ + public function update($update = null, $alias = null) + { + $this->type = self::UPDATE; + + if ( ! $update) { + return $this; + } + + return $this->add('from', array( + 'table' => $update, + 'alias' => $alias + )); + } + + /** + * Create and add a query root corresponding to the table identified by the + * given alias, forming a cartesian product with any existing query roots. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.id') + * ->from('users', 'u') + * + * + * @param string $from The table + * @param string $alias The alias of the table + * @return QueryBuilder This QueryBuilder instance. + */ + public function from($from, $alias) + { + return $this->add('from', array( + 'table' => $from, + 'alias' => $alias + ), true); + } + + /** + * Creates and adds a join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause + * @param string $join The table name to join + * @param string $alias The alias of the join table + * @param string $condition The condition for the join + * @return QueryBuilder This QueryBuilder instance. + */ + public function join($fromAlias, $join, $alias, $condition = null) + { + return $this->innerJoin($fromAlias, $join, $alias, $condition); + } + + /** + * Creates and adds a join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause + * @param string $join The table name to join + * @param string $alias The alias of the join table + * @param string $condition The condition for the join + * @return QueryBuilder This QueryBuilder instance. + */ + public function innerJoin($fromAlias, $join, $alias, $condition = null) + { + return $this->add('join', array( + $fromAlias => array( + 'joinType' => 'inner', + 'joinTable' => $join, + 'joinAlias' => $alias, + 'joinCondition' => $condition + ) + ), true); + } + + /** + * Creates and adds a left join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause + * @param string $join The table name to join + * @param string $alias The alias of the join table + * @param string $condition The condition for the join + * @return QueryBuilder This QueryBuilder instance. + */ + public function leftJoin($fromAlias, $join, $alias, $condition = null) + { + return $this->add('join', array( + $fromAlias => array( + 'joinType' => 'left', + 'joinTable' => $join, + 'joinAlias' => $alias, + 'joinCondition' => $condition + ) + ), true); + } + + /** + * Creates and adds a right join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause + * @param string $join The table name to join + * @param string $alias The alias of the join table + * @param string $condition The condition for the join + * @return QueryBuilder This QueryBuilder instance. + */ + public function rightJoin($fromAlias, $join, $alias, $condition = null) + { + return $this->add('join', array( + $fromAlias => array( + 'joinType' => 'right', + 'joinTable' => $join, + 'joinAlias' => $alias, + 'joinCondition' => $condition + ) + ), true); + } + + /** + * Sets a new value for a column in a bulk update query. + * + * + * $qb = $conn->createQueryBuilder() + * ->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $key The column to set. + * @param string $value The value, expression, placeholder, etc. + * @return QueryBuilder This QueryBuilder instance. + */ + public function set($key, $value) + { + return $this->add('set', $key .' = ' . $value, true); + } + + /** + * Specifies one or more restrictions to the query result. + * Replaces any previously specified restrictions, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->where('u.id = ?'); + * + * // You can optionally programatically build and/or expressions + * $qb = $conn->createQueryBuilder(); + * + * $or = $qb->expr()->orx(); + * $or->add($qb->expr()->eq('u.id', 1)); + * $or->add($qb->expr()->eq('u.id', 2)); + * + * $qb->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where($or); + * + * + * @param mixed $predicates The restriction predicates. + * @return QueryBuilder This QueryBuilder instance. + */ + public function where($predicates) + { + if ( ! (func_num_args() == 1 && $predicates instanceof CompositeExpression) ) { + $predicates = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + return $this->add('where', $predicates); + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * conjunction with any previously specified restrictions. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.username LIKE ?') + * ->andWhere('u.is_active = 1'); + * + * + * @param mixed $where The query restrictions. + * @return QueryBuilder This QueryBuilder instance. + * @see where() + */ + public function andWhere($where) + { + $where = $this->getQueryPart('where'); + $args = func_get_args(); + + if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new CompositeExpression(CompositeExpression::TYPE_AND, $args); + } + + return $this->add('where', $where, true); + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * disjunction with any previously specified restrictions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->where('u.id = 1') + * ->orWhere('u.id = 2'); + * + * + * @param mixed $where The WHERE statement + * @return QueryBuilder $qb + * @see where() + */ + public function orWhere($where) + { + $where = $this->getQueryPart('where'); + $args = func_get_args(); + + if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new CompositeExpression(CompositeExpression::TYPE_OR, $args); + } + + return $this->add('where', $where, true); + } + + /** + * Specifies a grouping over the results of the query. + * Replaces any previously specified groupings, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->groupBy('u.id'); + * + * + * @param mixed $groupBy The grouping expression. + * @return QueryBuilder This QueryBuilder instance. + */ + public function groupBy($groupBy) + { + if (empty($groupBy)) { + return $this; + } + + $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); + + return $this->add('groupBy', $groupBy, false); + } + + + /** + * Adds a grouping expression to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->groupBy('u.lastLogin'); + * ->addGroupBy('u.createdAt') + * + * + * @param mixed $groupBy The grouping expression. + * @return QueryBuilder This QueryBuilder instance. + */ + public function addGroupBy($groupBy) + { + if (empty($groupBy)) { + return $this; + } + + $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); + + return $this->add('groupBy', $groupBy, true); + } + + /** + * Specifies a restriction over the groups of the query. + * Replaces any previous having restrictions, if any. + * + * @param mixed $having The restriction over the groups. + * @return QueryBuilder This QueryBuilder instance. + */ + public function having($having) + { + if ( ! (func_num_args() == 1 && $having instanceof CompositeExpression)) { + $having = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + return $this->add('having', $having); + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * conjunction with any existing having restrictions. + * + * @param mixed $having The restriction to append. + * @return QueryBuilder This QueryBuilder instance. + */ + public function andHaving($having) + { + $having = $this->getQueryPart('having'); + $args = func_get_args(); + + if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) { + $having->addMultiple($args); + } else { + array_unshift($args, $having); + $having = new CompositeExpression(CompositeExpression::TYPE_AND, $args); + } + + return $this->add('having', $having); + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * disjunction with any existing having restrictions. + * + * @param mixed $having The restriction to add. + * @return QueryBuilder This QueryBuilder instance. + */ + public function orHaving($having) + { + $having = $this->getQueryPart('having'); + $args = func_get_args(); + + if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) { + $having->addMultiple($args); + } else { + array_unshift($args, $having); + $having = new CompositeExpression(CompositeExpression::TYPE_OR, $args); + } + + return $this->add('having', $having); + } + + /** + * Specifies an ordering for the query results. + * Replaces any previously specified orderings, if any. + * + * @param string $sort The ordering expression. + * @param string $order The ordering direction. + * @return QueryBuilder This QueryBuilder instance. + */ + public function orderBy($sort, $order = null) + { + return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), false); + } + + /** + * Adds an ordering to the query results. + * + * @param string $sort The ordering expression. + * @param string $order The ordering direction. + * @return QueryBuilder This QueryBuilder instance. + */ + public function addOrderBy($sort, $order = null) + { + return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), true); + } + + /** + * Get a query part by its name. + * + * @param string $queryPartName + * @return mixed $queryPart + */ + public function getQueryPart($queryPartName) + { + return $this->sqlParts[$queryPartName]; + } + + /** + * Get all query parts. + * + * @return array $sqlParts + */ + public function getQueryParts() + { + return $this->sqlParts; + } + + /** + * Reset SQL parts + * + * @param array $queryPartNames + * @return QueryBuilder + */ + public function resetQueryParts($queryPartNames = null) + { + if (is_null($queryPartNames)) { + $queryPartNames = array_keys($this->sqlParts); + } + + foreach ($queryPartNames as $queryPartName) { + $this->resetQueryPart($queryPartName); + } + + return $this; + } + + /** + * Reset single SQL part + * + * @param string $queryPartName + * @return QueryBuilder + */ + public function resetQueryPart($queryPartName) + { + $this->sqlParts[$queryPartName] = is_array($this->sqlParts[$queryPartName]) + ? array() : null; + + $this->state = self::STATE_DIRTY; + + return $this; + } + + /** + * Converts this instance into a SELECT string in SQL. + * + * @return string + */ + private function getSQLForSelect() + { + $query = 'SELECT ' . implode(', ', $this->sqlParts['select']) . ' FROM '; + + $fromClauses = array(); + + // Loop through all FROM clauses + foreach ($this->sqlParts['from'] as $from) { + $fromClause = $from['table'] . ' ' . $from['alias']; + + if (isset($this->sqlParts['join'][$from['alias']])) { + foreach ($this->sqlParts['join'][$from['alias']] as $join) { + $fromClause .= ' ' . strtoupper($join['joinType']) + . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias'] + . ' ON ' . ((string) $join['joinCondition']); + } + } + + $fromClauses[$from['alias']] = $fromClause; + } + + // loop through all JOIN clasues for validation purpose + foreach ($this->sqlParts['join'] as $fromAlias => $joins) { + if ( ! isset($fromClauses[$fromAlias]) ) { + throw QueryException::unknownFromAlias($fromAlias, array_keys($fromClauses)); + } + } + + $query .= implode(', ', $fromClauses) + . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '') + . ($this->sqlParts['groupBy'] ? ' GROUP BY ' . implode(', ', $this->sqlParts['groupBy']) : '') + . ($this->sqlParts['having'] !== null ? ' HAVING ' . ((string) $this->sqlParts['having']) : '') + . ($this->sqlParts['orderBy'] ? ' ORDER BY ' . implode(', ', $this->sqlParts['orderBy']) : ''); + + return ($this->maxResults === null && $this->firstResult == null) + ? $query + : $this->connection->getDatabasePlatform()->modifyLimitQuery($query, $this->maxResults, $this->firstResult); + } + + /** + * Converts this instance into an UPDATE string in SQL. + * + * @return string + */ + private function getSQLForUpdate() + { + $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); + $query = 'UPDATE ' . $table + . ' SET ' . implode(", ", $this->sqlParts['set']) + . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : ''); + + return $query; + } + + /** + * Converts this instance into a DELETE string in SQL. + * + * @return string + */ + private function getSQLForDelete() + { + $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); + $query = 'DELETE FROM ' . $table . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : ''); + + return $query; + } + + /** + * Gets a string representation of this QueryBuilder which corresponds to + * the final SQL query being constructed. + * + * @return string The string representation of this QueryBuilder. + */ + public function __toString() + { + return $this->getSQL(); + } + + /** + * Create a new named parameter and bind the value $value to it. + * + * This method provides a shortcut for PDOStatement::bindValue + * when using prepared statements. + * + * The parameter $value specifies the value that you want to bind. If + * $placeholder is not provided bindValue() will automatically create a + * placeholder for you. An automatic placeholder will be of the name + * ':dcValue1', ':dcValue2' etc. + * + * For more information see {@link http://php.net/pdostatement-bindparam} + * + * Example: + * + * $value = 2; + * $q->eq( 'id', $q->bindValue( $value ) ); + * $stmt = $q->executeQuery(); // executed with 'id = 2' + * + * + * @license New BSD License + * @link http://www.zetacomponents.org + * @param mixed $value + * @param mixed $type + * @param string $placeHolder the name to bind with. The string must start with a colon ':'. + * @return string the placeholder name used. + */ + public function createNamedParameter( $value, $type = \PDO::PARAM_STR, $placeHolder = null ) + { + if ( $placeHolder === null ) { + $this->boundCounter++; + $placeHolder = ":dcValue" . $this->boundCounter; + } + $this->setParameter(substr($placeHolder, 1), $value, $type); + + return $placeHolder; + } + + /** + * Create a new positional parameter and bind the given value to it. + * + * Attention: If you are using positional parameters with the query builder you have + * to be very careful to bind all parameters in the order they appear in the SQL + * statement , otherwise they get bound in the wrong order which can lead to serious + * bugs in your code. + * + * Example: + * + * $qb = $conn->createQueryBuilder(); + * $qb->select('u.*') + * ->from('users', 'u') + * ->where('u.username = ' . $qb->createPositionalParameter('Foo', PDO::PARAM_STR)) + * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', PDO::PARAM_STR)) + * + * + * @param mixed $value + * @param mixed $type + * @return string + */ + public function createPositionalParameter($value, $type = \PDO::PARAM_STR) + { + $this->boundCounter++; + $this->setParameter($this->boundCounter, $value, $type); + return "?"; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryException.php new file mode 100644 index 0000000..fe2cc2f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryException.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\DBAL\Query; + +use Doctrine\DBAL\DBALException; + +/** + * Driver interface. + * Interface that all DBAL drivers must implement. + * + * @since 2.1.4 + */ +class QueryException extends DBALException +{ + static public function unknownFromAlias($alias, $registeredAliases) + { + return new self("The given alias '" . $alias . "' is not part of " . + "any FROM clause table. The currently registered FROM-clause " . + "aliases are: " . implode(", ", $registeredAliases) . ". Join clauses " . + "are bound to from clauses to provide support for mixing of multiple " . + "from and join clauses."); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/README.markdown b/vendor/doctrine/dbal/lib/Doctrine/DBAL/README.markdown new file mode 100644 index 0000000..e69de29 diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php new file mode 100644 index 0000000..b3704df --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php @@ -0,0 +1,179 @@ +. + */ + + +namespace Doctrine\DBAL; + +use Doctrine\DBAL\Connection; + +/** + * Utility class that parses sql statements with regard to types and parameters. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class SQLParserUtils +{ + /** + * Get an array of the placeholders in an sql statements as keys and their positions in the query string. + * + * Returns an integer => integer pair (indexed from zero) for a positional statement + * and a string => int[] pair for a named statement. + * + * @param string $statement + * @param bool $isPositional + * @return array + */ + static public function getPlaceholderPositions($statement, $isPositional = true) + { + $match = ($isPositional) ? '?' : ':'; + if (strpos($statement, $match) === false) { + return array(); + } + + $count = 0; + $inLiteral = false; // a valid query never starts with quotes + $stmtLen = strlen($statement); + $paramMap = array(); + for ($i = 0; $i < $stmtLen; $i++) { + if ($statement[$i] == $match && !$inLiteral) { + // real positional parameter detected + if ($isPositional) { + $paramMap[$count] = $i; + } else { + $name = ""; + // TODO: Something faster/better to match this than regex? + for ($j = $i; ($j < $stmtLen && preg_match('(([:a-zA-Z0-9_]{1}))', $statement[$j])); $j++) { + $name .= $statement[$j]; + } + $paramMap[$name][] = $i; // named parameters can be duplicated! + $i = $j; + } + ++$count; + } else if ($statement[$i] == "'" || $statement[$i] == '"') { + $inLiteral = ! $inLiteral; // switch state! + } + } + + return $paramMap; + } + + /** + * For a positional query this method can rewrite the sql statement with regard to array parameters. + * + * @param string $query + * @param array $params + * @param array $types + */ + static public function expandListParameters($query, $params, $types) + { + $isPositional = is_int(key($params)); + $arrayPositions = array(); + $bindIndex = -1; + foreach ($types AS $name => $type) { + ++$bindIndex; + if ($type === Connection::PARAM_INT_ARRAY || $type === Connection::PARAM_STR_ARRAY) { + if ($isPositional) { + $name = $bindIndex; + } + + $arrayPositions[$name] = false; + } + } + + if ((!$arrayPositions && $isPositional) || (count($params) != count($types))) { + return array($query, $params, $types); + } + + $paramPos = self::getPlaceholderPositions($query, $isPositional); + if ($isPositional) { + $paramOffset = 0; + $queryOffset = 0; + foreach ($paramPos AS $needle => $needlePos) { + if (!isset($arrayPositions[$needle])) { + continue; + } + + $needle += $paramOffset; + $needlePos += $queryOffset; + $len = count($params[$needle]); + + $params = array_merge( + array_slice($params, 0, $needle), + $params[$needle], + array_slice($params, $needle + 1) + ); + + $types = array_merge( + array_slice($types, 0, $needle), + array_fill(0, $len, $types[$needle] - Connection::ARRAY_PARAM_OFFSET), // array needles are at PDO::PARAM_* + 100 + array_slice($types, $needle + 1) + ); + + $expandStr = implode(", ", array_fill(0, $len, "?")); + $query = substr($query, 0, $needlePos) . $expandStr . substr($query, $needlePos + 1); + + $paramOffset += ($len - 1); // Grows larger by number of parameters minus the replaced needle. + $queryOffset += (strlen($expandStr) - 1); + } + + } else { + $queryOffset= 0; + $typesOrd = array(); + $paramsOrd = array(); + foreach ($paramPos as $needle => $needlePos) { + $paramLen = strlen($needle); + $token = substr($needle,0,1); + $needle = substr($needle,1); + $value = $params[$needle]; + + if (!isset($arrayPositions[$needle])) { + foreach ($needlePos as $pos) { + $pos += $queryOffset; + $queryOffset -= ($paramLen - 1); + $paramsOrd[] = $value; + $typesOrd[] = $types[$needle]; + $query = substr($query, 0, $pos) . '?' . substr($query, ($pos + $paramLen)); + } + } else { + $len = count($value); + $expandStr = implode(", ", array_fill(0, $len, "?")); + foreach ($needlePos as $pos) { + + foreach ($value as $val) { + $paramsOrd[] = $val; + $typesOrd[] = $types[$needle] - Connection::ARRAY_PARAM_OFFSET; + } + + $pos += $queryOffset; + $queryOffset += (strlen($expandStr) - $paramLen); + $query = substr($query, 0, $pos) . $expandStr . substr($query, ($pos + $paramLen)); + } + } + } + + $types = $typesOrd; + $params = $paramsOrd; + } + + return array($query, $params, $types); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractAsset.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractAsset.php new file mode 100644 index 0000000..a14218a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractAsset.php @@ -0,0 +1,204 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * The abstract asset allows to reset the name of all assets without publishing this to the public userland. + * + * This encapsulation hack is necessary to keep a consistent state of the database schema. Say we have a list of tables + * array($tableName => Table($tableName)); if you want to rename the table, you have to make sure + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +abstract class AbstractAsset +{ + /** + * @var string + */ + protected $_name; + + /** + * Namespace of the asset. If none isset the default namespace is assumed. + * + * @var string + */ + protected $_namespace; + + /** + * @var bool + */ + protected $_quoted = false; + + /** + * Set name of this asset + * + * @param string $name + */ + protected function _setName($name) + { + if ($this->isQuoted($name)) { + $this->_quoted = true; + $name = $this->trimQuotes($name); + } + if (strpos($name, ".") !== false) { + $parts = explode(".", $name); + $this->_namespace = $parts[0]; + $name = $parts[1]; + } + $this->_name = $name; + } + + /** + * Is this asset in the default namespace? + * + * @param string $defaultNamespaceName + * @return bool + */ + public function isInDefaultNamespace($defaultNamespaceName) + { + return $this->_namespace == $defaultNamespaceName || $this->_namespace === null; + } + + /** + * Get namespace name of this asset. + * + * If NULL is returned this means the default namespace is used. + * + * @return string + */ + public function getNamespaceName() + { + return $this->_namespace; + } + + /** + * The shortest name is stripped of the default namespace. All other + * namespaced elements are returned as full-qualified names. + * + * @param string + * @return string + */ + public function getShortestName($defaultNamespaceName) + { + $shortestName = $this->getName(); + if ($this->_namespace == $defaultNamespaceName) { + $shortestName = $this->_name; + } + return strtolower($shortestName); + } + + /** + * The normalized name is full-qualified and lowerspaced. Lowerspacing is + * actually wrong, but we have to do it to keep our sanity. If you are + * using database objects that only differentiate in the casing (FOO vs + * Foo) then you will NOT be able to use Doctrine Schema abstraction. + * + * Every non-namespaced element is prefixed with the default namespace + * name which is passed as argument to this method. + * + * @return string + */ + public function getFullQualifiedName($defaultNamespaceName) + { + $name = $this->getName(); + if (!$this->_namespace) { + $name = $defaultNamespaceName . "." . $name; + } + return strtolower($name); + } + + /** + * Check if this identifier is quoted. + * + * @param string $identifier + * @return bool + */ + protected function isQuoted($identifier) + { + return (isset($identifier[0]) && ($identifier[0] == '`' || $identifier[0] == '"')); + } + + /** + * Trim quotes from the identifier. + * + * @param string $identifier + * @return string + */ + protected function trimQuotes($identifier) + { + return str_replace(array('`', '"'), '', $identifier); + } + + /** + * Return name of this schema asset. + * + * @return string + */ + public function getName() + { + if ($this->_namespace) { + return $this->_namespace . "." . $this->_name; + } + return $this->_name; + } + + /** + * Get the quoted representation of this asset but only if it was defined with one. Otherwise + * return the plain unquoted value as inserted. + * + * @param AbstractPlatform $platform + * @return string + */ + public function getQuotedName(AbstractPlatform $platform) + { + $keywords = $platform->getReservedKeywordsList(); + $parts = explode(".", $this->getName()); + foreach ($parts AS $k => $v) { + $parts[$k] = ($this->_quoted || $keywords->isKeyword($v)) ? $platform->quoteIdentifier($v) : $v; + } + + return implode(".", $parts); + } + + /** + * Generate an identifier from a list of column names obeying a certain string length. + * + * This is especially important for Oracle, since it does not allow identifiers larger than 30 chars, + * however building idents automatically for foreign keys, composite keys or such can easily create + * very long names. + * + * @param array $columnNames + * @param string $prefix + * @param int $maxSize + * @return string + */ + protected function _generateIdentifierName($columnNames, $prefix='', $maxSize=30) + { + $hash = implode("", array_map(function($column) { + return dechex(crc32($column)); + }, $columnNames)); + return substr(strtoupper($prefix . "_" . $hash), 0, $maxSize); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php new file mode 100644 index 0000000..57fd67a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php @@ -0,0 +1,890 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Events; +use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs; +use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; +use Doctrine\DBAL\Types; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Base class for schema managers. Schema managers are used to inspect and/or + * modify the database schema/structure. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Roman Borschel + * @author Jonathan H. Wage + * @author Benjamin Eberlei + * @since 2.0 + */ +abstract class AbstractSchemaManager +{ + /** + * Holds instance of the Doctrine connection for this schema manager + * + * @var \Doctrine\DBAL\Connection + */ + protected $_conn; + + /** + * Holds instance of the database platform used for this schema manager + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $_platform; + + /** + * Constructor. Accepts the Connection instance to manage the schema for + * + * @param \Doctrine\DBAL\Connection $conn + */ + public function __construct(\Doctrine\DBAL\Connection $conn) + { + $this->_conn = $conn; + $this->_platform = $this->_conn->getDatabasePlatform(); + } + + /** + * Return associated platform. + * + * @return \Doctrine\DBAL\Platform\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_platform; + } + + /** + * Try any method on the schema manager. Normally a method throws an + * exception when your DBMS doesn't support it or if an error occurs. + * This method allows you to try and method on your SchemaManager + * instance and will return false if it does not work or is not supported. + * + * + * $result = $sm->tryMethod('dropView', 'view_name'); + * + * + * @return mixed + */ + public function tryMethod() + { + $args = func_get_args(); + $method = $args[0]; + unset($args[0]); + $args = array_values($args); + + try { + return call_user_func_array(array($this, $method), $args); + } catch (\Exception $e) { + return false; + } + } + + /** + * List the available databases for this connection + * + * @return array $databases + */ + public function listDatabases() + { + $sql = $this->_platform->getListDatabasesSQL(); + + $databases = $this->_conn->fetchAll($sql); + + return $this->_getPortableDatabasesList($databases); + } + + /** + * List the available sequences for this connection + * + * @return Sequence[] + */ + public function listSequences($database = null) + { + if (is_null($database)) { + $database = $this->_conn->getDatabase(); + } + $sql = $this->_platform->getListSequencesSQL($database); + + $sequences = $this->_conn->fetchAll($sql); + + return $this->filterAssetNames($this->_getPortableSequencesList($sequences)); + } + + /** + * List the columns for a given table. + * + * In contrast to other libraries and to the old version of Doctrine, + * this column definition does try to contain the 'primary' field for + * the reason that it is not portable accross different RDBMS. Use + * {@see listTableIndexes($tableName)} to retrieve the primary key + * of a table. We're a RDBMS specifies more details these are held + * in the platformDetails array. + * + * @param string $table The name of the table. + * @param string $database + * @return Column[] + */ + public function listTableColumns($table, $database = null) + { + if (!$database) { + $database = $this->_conn->getDatabase(); + } + + $sql = $this->_platform->getListTableColumnsSQL($table, $database); + + $tableColumns = $this->_conn->fetchAll($sql); + + return $this->_getPortableTableColumnList($table, $database, $tableColumns); + } + + /** + * List the indexes for a given table returning an array of Index instances. + * + * Keys of the portable indexes list are all lower-cased. + * + * @param string $table The name of the table + * @return Index[] $tableIndexes + */ + public function listTableIndexes($table) + { + $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); + + $tableIndexes = $this->_conn->fetchAll($sql); + + return $this->_getPortableTableIndexesList($tableIndexes, $table); + } + + /** + * Return true if all the given tables exist. + * + * @param array $tableNames + * @return bool + */ + public function tablesExist($tableNames) + { + $tableNames = array_map('strtolower', (array)$tableNames); + return count($tableNames) == count(\array_intersect($tableNames, array_map('strtolower', $this->listTableNames()))); + } + + /** + * Return a list of all tables in the current database + * + * @return array + */ + public function listTableNames() + { + $sql = $this->_platform->getListTablesSQL(); + + $tables = $this->_conn->fetchAll($sql); + $tableNames = $this->_getPortableTablesList($tables); + return $this->filterAssetNames($tableNames); + } + + /** + * Filter asset names if they are configured to return only a subset of all + * the found elements. + * + * @param array $assetNames + * @return array + */ + protected function filterAssetNames($assetNames) + { + $filterExpr = $this->getFilterSchemaAssetsExpression(); + if (!$filterExpr) { + return $assetNames; + } + return array_values ( + array_filter($assetNames, function ($assetName) use ($filterExpr) { + $assetName = ($assetName instanceof AbstractAsset) ? $assetName->getName() : $assetName; + return preg_match('(' . $filterExpr . ')', $assetName); + }) + ); + } + + protected function getFilterSchemaAssetsExpression() + { + return $this->_conn->getConfiguration()->getFilterSchemaAssetsExpression(); + } + + /** + * List the tables for this connection + * + * @return Table[] + */ + public function listTables() + { + $tableNames = $this->listTableNames(); + + $tables = array(); + foreach ($tableNames AS $tableName) { + $tables[] = $this->listTableDetails($tableName); + } + + return $tables; + } + + /** + * @param string $tableName + * @return Table + */ + public function listTableDetails($tableName) + { + $columns = $this->listTableColumns($tableName); + $foreignKeys = array(); + if ($this->_platform->supportsForeignKeyConstraints()) { + $foreignKeys = $this->listTableForeignKeys($tableName); + } + $indexes = $this->listTableIndexes($tableName); + + return new Table($tableName, $columns, $indexes, $foreignKeys, false, array()); + } + + /** + * List the views this connection has + * + * @return View[] + */ + public function listViews() + { + $database = $this->_conn->getDatabase(); + $sql = $this->_platform->getListViewsSQL($database); + $views = $this->_conn->fetchAll($sql); + + return $this->_getPortableViewsList($views); + } + + /** + * List the foreign keys for the given table + * + * @param string $table The name of the table + * @return ForeignKeyConstraint[] + */ + public function listTableForeignKeys($table, $database = null) + { + if (is_null($database)) { + $database = $this->_conn->getDatabase(); + } + $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); + $tableForeignKeys = $this->_conn->fetchAll($sql); + + return $this->_getPortableTableForeignKeysList($tableForeignKeys); + } + + /* drop*() Methods */ + + /** + * Drops a database. + * + * NOTE: You can not drop the database this SchemaManager is currently connected to. + * + * @param string $database The name of the database to drop + */ + public function dropDatabase($database) + { + $this->_execSql($this->_platform->getDropDatabaseSQL($database)); + } + + /** + * Drop the given table + * + * @param string $table The name of the table to drop + */ + public function dropTable($table) + { + $this->_execSql($this->_platform->getDropTableSQL($table)); + } + + /** + * Drop the index from the given table + * + * @param Index|string $index The name of the index + * @param string|Table $table The name of the table + */ + public function dropIndex($index, $table) + { + if($index instanceof Index) { + $index = $index->getQuotedName($this->_platform); + } + + $this->_execSql($this->_platform->getDropIndexSQL($index, $table)); + } + + /** + * Drop the constraint from the given table + * + * @param Constraint $constraint + * @param string $table The name of the table + */ + public function dropConstraint(Constraint $constraint, $table) + { + $this->_execSql($this->_platform->getDropConstraintSQL($constraint, $table)); + } + + /** + * Drops a foreign key from a table. + * + * @param ForeignKeyConstraint|string $table The name of the table with the foreign key. + * @param Table|string $name The name of the foreign key. + * @return boolean $result + */ + public function dropForeignKey($foreignKey, $table) + { + $this->_execSql($this->_platform->getDropForeignKeySQL($foreignKey, $table)); + } + + /** + * Drops a sequence with a given name. + * + * @param string $name The name of the sequence to drop. + */ + public function dropSequence($name) + { + $this->_execSql($this->_platform->getDropSequenceSQL($name)); + } + + /** + * Drop a view + * + * @param string $name The name of the view + * @return boolean $result + */ + public function dropView($name) + { + $this->_execSql($this->_platform->getDropViewSQL($name)); + } + + /* create*() Methods */ + + /** + * Creates a new database. + * + * @param string $database The name of the database to create. + */ + public function createDatabase($database) + { + $this->_execSql($this->_platform->getCreateDatabaseSQL($database)); + } + + /** + * Create a new table. + * + * @param Table $table + * @param int $createFlags + */ + public function createTable(Table $table) + { + $createFlags = AbstractPlatform::CREATE_INDEXES|AbstractPlatform::CREATE_FOREIGNKEYS; + $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags)); + } + + /** + * Create a new sequence + * + * @param Sequence $sequence + * @throws Doctrine\DBAL\ConnectionException if something fails at database level + */ + public function createSequence($sequence) + { + $this->_execSql($this->_platform->getCreateSequenceSQL($sequence)); + } + + /** + * Create a constraint on a table + * + * @param Constraint $constraint + * @param string|Table $table + */ + public function createConstraint(Constraint $constraint, $table) + { + $this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table)); + } + + /** + * Create a new index on a table + * + * @param Index $index + * @param string $table name of the table on which the index is to be created + */ + public function createIndex(Index $index, $table) + { + $this->_execSql($this->_platform->getCreateIndexSQL($index, $table)); + } + + /** + * Create a new foreign key + * + * @param ForeignKeyConstraint $foreignKey ForeignKey instance + * @param string|Table $table name of the table on which the foreign key is to be created + */ + public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table)); + } + + /** + * Create a new view + * + * @param View $view + */ + public function createView(View $view) + { + $this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql())); + } + + /* dropAndCreate*() Methods */ + + /** + * Drop and create a constraint + * + * @param Constraint $constraint + * @param string $table + * @see dropConstraint() + * @see createConstraint() + */ + public function dropAndCreateConstraint(Constraint $constraint, $table) + { + $this->tryMethod('dropConstraint', $constraint, $table); + $this->createConstraint($constraint, $table); + } + + /** + * Drop and create a new index on a table + * + * @param string|Table $table name of the table on which the index is to be created + * @param Index $index + */ + public function dropAndCreateIndex(Index $index, $table) + { + $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table); + $this->createIndex($index, $table); + } + + /** + * Drop and create a new foreign key + * + * @param ForeignKeyConstraint $foreignKey associative array that defines properties of the foreign key to be created. + * @param string|Table $table name of the table on which the foreign key is to be created + */ + public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $this->tryMethod('dropForeignKey', $foreignKey, $table); + $this->createForeignKey($foreignKey, $table); + } + + /** + * Drop and create a new sequence + * + * @param Sequence $sequence + * @throws Doctrine\DBAL\ConnectionException if something fails at database level + */ + public function dropAndCreateSequence(Sequence $sequence) + { + $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform)); + $this->createSequence($sequence); + } + + /** + * Drop and create a new table. + * + * @param Table $table + */ + public function dropAndCreateTable(Table $table) + { + $this->tryMethod('dropTable', $table->getQuotedName($this->_platform)); + $this->createTable($table); + } + + /** + * Drop and creates a new database. + * + * @param string $database The name of the database to create. + */ + public function dropAndCreateDatabase($database) + { + $this->tryMethod('dropDatabase', $database); + $this->createDatabase($database); + } + + /** + * Drop and create a new view + * + * @param View $view + */ + public function dropAndCreateView(View $view) + { + $this->tryMethod('dropView', $view->getQuotedName($this->_platform)); + $this->createView($view); + } + + /* alterTable() Methods */ + + /** + * Alter an existing tables schema + * + * @param TableDiff $tableDiff + */ + public function alterTable(TableDiff $tableDiff) + { + $queries = $this->_platform->getAlterTableSQL($tableDiff); + if (is_array($queries) && count($queries)) { + foreach ($queries AS $ddlQuery) { + $this->_execSql($ddlQuery); + } + } + } + + /** + * Rename a given table to another name + * + * @param string $name The current name of the table + * @param string $newName The new name of the table + */ + public function renameTable($name, $newName) + { + $tableDiff = new TableDiff($name); + $tableDiff->newName = $newName; + $this->alterTable($tableDiff); + } + + /** + * Methods for filtering return values of list*() methods to convert + * the native DBMS data definition to a portable Doctrine definition + */ + + protected function _getPortableDatabasesList($databases) + { + $list = array(); + foreach ($databases as $key => $value) { + if ($value = $this->_getPortableDatabaseDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableDatabaseDefinition($database) + { + return $database; + } + + protected function _getPortableFunctionsList($functions) + { + $list = array(); + foreach ($functions as $key => $value) { + if ($value = $this->_getPortableFunctionDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableFunctionDefinition($function) + { + return $function; + } + + protected function _getPortableTriggersList($triggers) + { + $list = array(); + foreach ($triggers as $key => $value) { + if ($value = $this->_getPortableTriggerDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableTriggerDefinition($trigger) + { + return $trigger; + } + + protected function _getPortableSequencesList($sequences) + { + $list = array(); + foreach ($sequences as $key => $value) { + if ($value = $this->_getPortableSequenceDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + /** + * @param array $sequence + * @return Sequence + */ + protected function _getPortableSequenceDefinition($sequence) + { + throw DBALException::notSupported('Sequences'); + } + + /** + * Independent of the database the keys of the column list result are lowercased. + * + * The name of the created column instance however is kept in its case. + * + * @param string $table The name of the table. + * @param string $database + * @param array $tableColumns + * @return array + */ + protected function _getPortableTableColumnList($table, $database, $tableColumns) + { + $eventManager = $this->_platform->getEventManager(); + + $list = array(); + foreach ($tableColumns as $key => $tableColumn) { + $column = null; + $defaultPrevented = false; + + if (null !== $eventManager && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) { + $eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn); + $eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs); + + $defaultPrevented = $eventArgs->isDefaultPrevented(); + $column = $eventArgs->getColumn(); + } + + if (!$defaultPrevented) { + $column = $this->_getPortableTableColumnDefinition($tableColumn); + } + + if ($column) { + $name = strtolower($column->getQuotedName($this->_platform)); + $list[$name] = $column; + } + } + return $list; + } + + /** + * Get Table Column Definition + * + * @param array $tableColumn + * @return Column + */ + abstract protected function _getPortableTableColumnDefinition($tableColumn); + + /** + * Aggregate and group the index results according to the required data result. + * + * @param array $tableIndexRows + * @param string $tableName + * @return array + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName=null) + { + $result = array(); + foreach($tableIndexRows AS $tableIndex) { + $indexName = $keyName = $tableIndex['key_name']; + if($tableIndex['primary']) { + $keyName = 'primary'; + } + $keyName = strtolower($keyName); + + if(!isset($result[$keyName])) { + $result[$keyName] = array( + 'name' => $indexName, + 'columns' => array($tableIndex['column_name']), + 'unique' => $tableIndex['non_unique'] ? false : true, + 'primary' => $tableIndex['primary'], + ); + } else { + $result[$keyName]['columns'][] = $tableIndex['column_name']; + } + } + + $eventManager = $this->_platform->getEventManager(); + + $indexes = array(); + foreach($result AS $indexKey => $data) { + $index = null; + $defaultPrevented = false; + + if (null !== $eventManager && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) { + $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn); + $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs); + + $defaultPrevented = $eventArgs->isDefaultPrevented(); + $index = $eventArgs->getIndex(); + } + + if (!$defaultPrevented) { + $index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary']); + } + + if ($index) { + $indexes[$indexKey] = $index; + } + } + + return $indexes; + } + + protected function _getPortableTablesList($tables) + { + $list = array(); + foreach ($tables as $key => $value) { + if ($value = $this->_getPortableTableDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableTableDefinition($table) + { + return $table; + } + + protected function _getPortableUsersList($users) + { + $list = array(); + foreach ($users as $key => $value) { + if ($value = $this->_getPortableUserDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableUserDefinition($user) + { + return $user; + } + + protected function _getPortableViewsList($views) + { + $list = array(); + foreach ($views as $key => $value) { + if ($view = $this->_getPortableViewDefinition($value)) { + $viewName = strtolower($view->getQuotedName($this->_platform)); + $list[$viewName] = $view; + } + } + return $list; + } + + protected function _getPortableViewDefinition($view) + { + return false; + } + + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $key => $value) { + if ($value = $this->_getPortableTableForeignKeyDefinition($value)) { + $list[] = $value; + } + } + return $list; + } + + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return $tableForeignKey; + } + + protected function _execSql($sql) + { + foreach ((array) $sql as $query) { + $this->_conn->executeUpdate($query); + } + } + + /** + * Create a schema instance for the current database. + * + * @return Schema + */ + public function createSchema() + { + $sequences = array(); + if($this->_platform->supportsSequences()) { + $sequences = $this->listSequences(); + } + $tables = $this->listTables(); + + return new Schema($tables, $sequences, $this->createSchemaConfig()); + } + + /** + * Create the configuration for this schema. + * + * @return SchemaConfig + */ + public function createSchemaConfig() + { + $schemaConfig = new SchemaConfig(); + $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength()); + + $searchPaths = $this->getSchemaSearchPaths(); + if (isset($searchPaths[0])) { + $schemaConfig->setName($searchPaths[0]); + } + + return $schemaConfig; + } + + /** + * The search path for namespaces in the currently connected database. + * + * The first entry is usually the default namespace in the Schema. All + * further namespaces contain tables/sequences which can also be addressed + * with a short, not full-qualified name. + * + * For databases that don't support subschema/namespaces this method + * returns the name of the currently connected database. + * + * @return array + */ + public function getSchemaSearchPaths() + { + return array($this->_conn->getDatabase()); + } + + /** + * Given a table comment this method tries to extract a typehint for Doctrine Type, or returns + * the type given as default. + * + * @param string $comment + * @param string $currentType + * @return string + */ + public function extractDoctrineTypeFromComment($comment, $currentType) + { + if (preg_match("(\(DC2Type:([a-zA-Z0-9]+)\))", $comment, $match)) { + $currentType = $match[1]; + } + return $currentType; + } + + public function removeDoctrineTypeFromComment($comment, $type) + { + return str_replace('(DC2Type:'.$type.')', '', $comment); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php new file mode 100644 index 0000000..cb28bc8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php @@ -0,0 +1,423 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use \Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Schema\Visitor\Visitor; + +/** + * Object representation of a database column + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class Column extends AbstractAsset +{ + /** + * @var \Doctrine\DBAL\Types\Type + */ + protected $_type; + + /** + * @var int + */ + protected $_length = null; + + /** + * @var int + */ + protected $_precision = 10; + + /** + * @var int + */ + protected $_scale = 0; + + /** + * @var bool + */ + protected $_unsigned = false; + + /** + * @var bool + */ + protected $_fixed = false; + + /** + * @var bool + */ + protected $_notnull = true; + + /** + * @var string + */ + protected $_default = null; + + /** + * @var bool + */ + protected $_autoincrement = false; + + /** + * @var array + */ + protected $_platformOptions = array(); + + /** + * @var string + */ + protected $_columnDefinition = null; + + /** + * @var string + */ + protected $_comment = null; + + /** + * @var array + */ + protected $_customSchemaOptions = array(); + + /** + * Create a new Column + * + * @param string $columnName + * @param Doctrine\DBAL\Types\Type $type + * @param int $length + * @param bool $notNull + * @param mixed $default + * @param bool $unsigned + * @param bool $fixed + * @param int $precision + * @param int $scale + * @param array $platformOptions + */ + public function __construct($columnName, Type $type, array $options=array()) + { + $this->_setName($columnName); + $this->setType($type); + $this->setOptions($options); + } + + /** + * @param array $options + * @return Column + */ + public function setOptions(array $options) + { + foreach ($options AS $name => $value) { + $method = "set".$name; + if (method_exists($this, $method)) { + $this->$method($value); + } + } + return $this; + } + + /** + * @param Type $type + * @return Column + */ + public function setType(Type $type) + { + $this->_type = $type; + return $this; + } + + /** + * @param int $length + * @return Column + */ + public function setLength($length) + { + if($length !== null) { + $this->_length = (int)$length; + } else { + $this->_length = null; + } + return $this; + } + + /** + * @param int $precision + * @return Column + */ + public function setPrecision($precision) + { + if (!is_numeric($precision)) { + $precision = 10; // defaults to 10 when no valid precision is given. + } + + $this->_precision = (int)$precision; + return $this; + } + + /** + * @param int $scale + * @return Column + */ + public function setScale($scale) + { + if (!is_numeric($scale)) { + $scale = 0; + } + + $this->_scale = (int)$scale; + return $this; + } + + /** + * + * @param bool $unsigned + * @return Column + */ + public function setUnsigned($unsigned) + { + $this->_unsigned = (bool)$unsigned; + return $this; + } + + /** + * + * @param bool $fixed + * @return Column + */ + public function setFixed($fixed) + { + $this->_fixed = (bool)$fixed; + return $this; + } + + /** + * @param bool $notnull + * @return Column + */ + public function setNotnull($notnull) + { + $this->_notnull = (bool)$notnull; + return $this; + } + + /** + * + * @param mixed $default + * @return Column + */ + public function setDefault($default) + { + $this->_default = $default; + return $this; + } + + /** + * + * @param array $platformOptions + * @return Column + */ + public function setPlatformOptions(array $platformOptions) + { + $this->_platformOptions = $platformOptions; + return $this; + } + + /** + * + * @param string $name + * @param mixed $value + * @return Column + */ + public function setPlatformOption($name, $value) + { + $this->_platformOptions[$name] = $value; + return $this; + } + + /** + * + * @param string + * @return Column + */ + public function setColumnDefinition($value) + { + $this->_columnDefinition = $value; + return $this; + } + + public function getType() + { + return $this->_type; + } + + public function getLength() + { + return $this->_length; + } + + public function getPrecision() + { + return $this->_precision; + } + + public function getScale() + { + return $this->_scale; + } + + public function getUnsigned() + { + return $this->_unsigned; + } + + public function getFixed() + { + return $this->_fixed; + } + + public function getNotnull() + { + return $this->_notnull; + } + + public function getDefault() + { + return $this->_default; + } + + public function getPlatformOptions() + { + return $this->_platformOptions; + } + + public function hasPlatformOption($name) + { + return isset($this->_platformOptions[$name]); + } + + public function getPlatformOption($name) + { + return $this->_platformOptions[$name]; + } + + public function getColumnDefinition() + { + return $this->_columnDefinition; + } + + public function getAutoincrement() + { + return $this->_autoincrement; + } + + public function setAutoincrement($flag) + { + $this->_autoincrement = $flag; + return $this; + } + + public function setComment($comment) + { + $this->_comment = $comment; + return $this; + } + + public function getComment() + { + return $this->_comment; + } + + /** + * @param string $name + * @param mixed $value + * @return Column + */ + public function setCustomSchemaOption($name, $value) + { + $this->_customSchemaOptions[$name] = $value; + return $this; + } + + /** + * @param string $name + * @return boolean + */ + public function hasCustomSchemaOption($name) + { + return isset($this->_customSchemaOptions[$name]); + } + + /** + * @param string $name + * @return mixed + */ + public function getCustomSchemaOption($name) + { + return $this->_customSchemaOptions[$name]; + } + + /** + * @param array $customSchemaOptions + * @return Column + */ + public function setCustomSchemaOptions(array $customSchemaOptions) + { + $this->_customSchemaOptions = $customSchemaOptions; + return $this; + } + + /** + * @return array + */ + public function getCustomSchemaOptions() + { + return $this->_customSchemaOptions; + } + + /** + * @param Visitor $visitor + */ + public function visit(\Doctrine\DBAL\Schema\Visitor $visitor) + { + $visitor->accept($this); + } + + /** + * @return array + */ + public function toArray() + { + return array_merge(array( + 'name' => $this->_name, + 'type' => $this->_type, + 'default' => $this->_default, + 'notnull' => $this->_notnull, + 'length' => $this->_length, + 'precision' => $this->_precision, + 'scale' => $this->_scale, + 'fixed' => $this->_fixed, + 'unsigned' => $this->_unsigned, + 'autoincrement' => $this->_autoincrement, + 'columnDefinition' => $this->_columnDefinition, + 'comment' => $this->_comment, + ), $this->_platformOptions, $this->_customSchemaOptions); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php new file mode 100644 index 0000000..4cf8969 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Represent the change of a column + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class ColumnDiff +{ + public $oldColumnName; + + /** + * @var Column + */ + public $column; + + /** + * @var array + */ + public $changedProperties = array(); + + public function __construct($oldColumnName, Column $column, array $changedProperties = array()) + { + $this->oldColumnName = $oldColumnName; + $this->column = $column; + $this->changedProperties = $changedProperties; + } + + public function hasChanged($propertyName) + { + return in_array($propertyName, $this->changedProperties); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php new file mode 100644 index 0000000..52b8ad8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php @@ -0,0 +1,399 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Compare to Schemas and return an instance of SchemaDiff + * + * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class Comparator +{ + /** + * @param Schema $fromSchema + * @param Schema $toSchema + * @return SchemaDiff + */ + static public function compareSchemas( Schema $fromSchema, Schema $toSchema ) + { + $c = new self(); + return $c->compare($fromSchema, $toSchema); + } + + /** + * Returns a SchemaDiff object containing the differences between the schemas $fromSchema and $toSchema. + * + * The returned diferences are returned in such a way that they contain the + * operations to change the schema stored in $fromSchema to the schema that is + * stored in $toSchema. + * + * @param Schema $fromSchema + * @param Schema $toSchema + * + * @return SchemaDiff + */ + public function compare(Schema $fromSchema, Schema $toSchema) + { + $diff = new SchemaDiff(); + + $foreignKeysToTable = array(); + + foreach ( $toSchema->getTables() AS $table ) { + $tableName = $table->getShortestName($toSchema->getName()); + if ( ! $fromSchema->hasTable($tableName)) { + $diff->newTables[$tableName] = $toSchema->getTable($tableName); + } else { + $tableDifferences = $this->diffTable($fromSchema->getTable($tableName), $toSchema->getTable($tableName)); + if ($tableDifferences !== false) { + $diff->changedTables[$tableName] = $tableDifferences; + } + } + } + + /* Check if there are tables removed */ + foreach ($fromSchema->getTables() AS $table) { + $tableName = $table->getShortestName($fromSchema->getName()); + + $table = $fromSchema->getTable($tableName); + if ( ! $toSchema->hasTable($tableName) ) { + $diff->removedTables[$tableName] = $table; + } + + // also remember all foreign keys that point to a specific table + foreach ($table->getForeignKeys() AS $foreignKey) { + $foreignTable = strtolower($foreignKey->getForeignTableName()); + if (!isset($foreignKeysToTable[$foreignTable])) { + $foreignKeysToTable[$foreignTable] = array(); + } + $foreignKeysToTable[$foreignTable][] = $foreignKey; + } + } + + foreach ($diff->removedTables AS $tableName => $table) { + if (isset($foreignKeysToTable[$tableName])) { + $diff->orphanedForeignKeys = array_merge($diff->orphanedForeignKeys, $foreignKeysToTable[$tableName]); + } + } + + foreach ($toSchema->getSequences() AS $sequence) { + $sequenceName = $sequence->getShortestName($toSchema->getName()); + if (!$fromSchema->hasSequence($sequenceName)) { + $diff->newSequences[] = $sequence; + } else { + if ($this->diffSequence($sequence, $fromSchema->getSequence($sequenceName))) { + $diff->changedSequences[] = $toSchema->getSequence($sequenceName); + } + } + } + + foreach ($fromSchema->getSequences() AS $sequence) { + $sequenceName = $sequence->getShortestName($fromSchema->getName()); + if (!$toSchema->hasSequence($sequenceName)) { + $diff->removedSequences[] = $sequence; + } + } + + return $diff; + } + + /** + * + * @param Sequence $sequence1 + * @param Sequence $sequence2 + */ + public function diffSequence(Sequence $sequence1, Sequence $sequence2) + { + if($sequence1->getAllocationSize() != $sequence2->getAllocationSize()) { + return true; + } + + if($sequence1->getInitialValue() != $sequence2->getInitialValue()) { + return true; + } + + return false; + } + + /** + * Returns the difference between the tables $table1 and $table2. + * + * If there are no differences this method returns the boolean false. + * + * @param Table $table1 + * @param Table $table2 + * + * @return bool|TableDiff + */ + public function diffTable(Table $table1, Table $table2) + { + $changes = 0; + $tableDifferences = new TableDiff($table1->getName()); + + $table1Columns = $table1->getColumns(); + $table2Columns = $table2->getColumns(); + + /* See if all the fields in table 1 exist in table 2 */ + foreach ( $table2Columns as $columnName => $column ) { + if ( !$table1->hasColumn($columnName) ) { + $tableDifferences->addedColumns[$columnName] = $column; + $changes++; + } + } + /* See if there are any removed fields in table 2 */ + foreach ( $table1Columns as $columnName => $column ) { + if ( !$table2->hasColumn($columnName) ) { + $tableDifferences->removedColumns[$columnName] = $column; + $changes++; + } + } + + foreach ( $table1Columns as $columnName => $column ) { + if ( $table2->hasColumn($columnName) ) { + $changedProperties = $this->diffColumn( $column, $table2->getColumn($columnName) ); + if (count($changedProperties) ) { + $columnDiff = new ColumnDiff($column->getName(), $table2->getColumn($columnName), $changedProperties); + $tableDifferences->changedColumns[$column->getName()] = $columnDiff; + $changes++; + } + } + } + + $this->detectColumnRenamings($tableDifferences); + + $table1Indexes = $table1->getIndexes(); + $table2Indexes = $table2->getIndexes(); + + foreach ($table2Indexes AS $index2Name => $index2Definition) { + foreach ($table1Indexes AS $index1Name => $index1Definition) { + if ($this->diffIndex($index1Definition, $index2Definition) === false) { + unset($table1Indexes[$index1Name]); + unset($table2Indexes[$index2Name]); + } else { + if ($index1Name == $index2Name) { + $tableDifferences->changedIndexes[$index2Name] = $table2Indexes[$index2Name]; + unset($table1Indexes[$index1Name]); + unset($table2Indexes[$index2Name]); + $changes++; + } + } + } + } + + foreach ($table1Indexes AS $index1Name => $index1Definition) { + $tableDifferences->removedIndexes[$index1Name] = $index1Definition; + $changes++; + } + + foreach ($table2Indexes AS $index2Name => $index2Definition) { + $tableDifferences->addedIndexes[$index2Name] = $index2Definition; + $changes++; + } + + $fromFkeys = $table1->getForeignKeys(); + $toFkeys = $table2->getForeignKeys(); + + foreach ($fromFkeys AS $key1 => $constraint1) { + foreach ($toFkeys AS $key2 => $constraint2) { + if($this->diffForeignKey($constraint1, $constraint2) === false) { + unset($fromFkeys[$key1]); + unset($toFkeys[$key2]); + } else { + if (strtolower($constraint1->getName()) == strtolower($constraint2->getName())) { + $tableDifferences->changedForeignKeys[] = $constraint2; + $changes++; + unset($fromFkeys[$key1]); + unset($toFkeys[$key2]); + } + } + } + } + + foreach ($fromFkeys AS $key1 => $constraint1) { + $tableDifferences->removedForeignKeys[] = $constraint1; + $changes++; + } + + foreach ($toFkeys AS $key2 => $constraint2) { + $tableDifferences->addedForeignKeys[] = $constraint2; + $changes++; + } + + return $changes ? $tableDifferences : false; + } + + /** + * Try to find columns that only changed their name, rename operations maybe cheaper than add/drop + * however ambiguouties between different possibilites should not lead to renaming at all. + * + * @param TableDiff $tableDifferences + */ + private function detectColumnRenamings(TableDiff $tableDifferences) + { + $renameCandidates = array(); + foreach ($tableDifferences->addedColumns AS $addedColumnName => $addedColumn) { + foreach ($tableDifferences->removedColumns AS $removedColumnName => $removedColumn) { + if (count($this->diffColumn($addedColumn, $removedColumn)) == 0) { + $renameCandidates[$addedColumn->getName()][] = array($removedColumn, $addedColumn, $addedColumnName); + } + } + } + + foreach ($renameCandidates AS $candidate => $candidateColumns) { + if (count($candidateColumns) == 1) { + list($removedColumn, $addedColumn) = $candidateColumns[0]; + $removedColumnName = strtolower($removedColumn->getName()); + $addedColumnName = strtolower($addedColumn->getName()); + + $tableDifferences->renamedColumns[$removedColumnName] = $addedColumn; + unset($tableDifferences->addedColumns[$addedColumnName]); + unset($tableDifferences->removedColumns[$removedColumnName]); + } + } + } + + /** + * @param ForeignKeyConstraint $key1 + * @param ForeignKeyConstraint $key2 + * @return bool + */ + public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2) + { + if (array_map('strtolower', $key1->getLocalColumns()) != array_map('strtolower', $key2->getLocalColumns())) { + return true; + } + + if (array_map('strtolower', $key1->getForeignColumns()) != array_map('strtolower', $key2->getForeignColumns())) { + return true; + } + + if ($key1->onUpdate() != $key2->onUpdate()) { + return true; + } + + if ($key1->onDelete() != $key2->onDelete()) { + return true; + } + + return false; + } + + /** + * Returns the difference between the fields $field1 and $field2. + * + * If there are differences this method returns $field2, otherwise the + * boolean false. + * + * @param Column $column1 + * @param Column $column2 + * + * @return array + */ + public function diffColumn(Column $column1, Column $column2) + { + $changedProperties = array(); + if ( $column1->getType() != $column2->getType() ) { + $changedProperties[] = 'type'; + } + + if ($column1->getNotnull() != $column2->getNotnull()) { + $changedProperties[] = 'notnull'; + } + + if ($column1->getDefault() != $column2->getDefault()) { + $changedProperties[] = 'default'; + } + + if ($column1->getUnsigned() != $column2->getUnsigned()) { + $changedProperties[] = 'unsigned'; + } + + if ($column1->getType() instanceof \Doctrine\DBAL\Types\StringType) { + // check if value of length is set at all, default value assumed otherwise. + $length1 = $column1->getLength() ?: 255; + $length2 = $column2->getLength() ?: 255; + if ($length1 != $length2) { + $changedProperties[] = 'length'; + } + + if ($column1->getFixed() != $column2->getFixed()) { + $changedProperties[] = 'fixed'; + } + } + + if ($column1->getType() instanceof \Doctrine\DBAL\Types\DecimalType) { + if (($column1->getPrecision()?:10) != ($column2->getPrecision()?:10)) { + $changedProperties[] = 'precision'; + } + if ($column1->getScale() != $column2->getScale()) { + $changedProperties[] = 'scale'; + } + } + + if ($column1->getAutoincrement() != $column2->getAutoincrement()) { + $changedProperties[] = 'autoincrement'; + } + + // only allow to delete comment if its set to '' not to null. + if ($column1->getComment() !== null && $column1->getComment() != $column2->getComment()) { + $changedProperties[] = 'comment'; + } + + $options1 = $column1->getCustomSchemaOptions(); + $options2 = $column2->getCustomSchemaOptions(); + + $commonKeys = array_keys(array_intersect_key($options1, $options2)); + + foreach ($commonKeys as $key) { + if ($options1[$key] !== $options2[$key]) { + $changedProperties[] = $key; + } + } + + $diffKeys = array_keys(array_diff_key($options1, $options2) + array_diff_key($options2, $options1)); + + $changedProperties = array_merge($changedProperties, $diffKeys); + + return $changedProperties; + } + + /** + * Finds the difference between the indexes $index1 and $index2. + * + * Compares $index1 with $index2 and returns $index2 if there are any + * differences or false in case there are no differences. + * + * @param Index $index1 + * @param Index $index2 + * @return bool + */ + public function diffIndex(Index $index1, Index $index2) + { + if ($index1->isFullfilledBy($index2) && $index2->isFullfilledBy($index1)) { + return false; + } + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php new file mode 100644 index 0000000..9e760ff --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Marker interface for contraints + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +interface Constraint +{ + public function getName(); + + public function getColumns(); +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php new file mode 100644 index 0000000..4e41eff --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php @@ -0,0 +1,214 @@ +. +*/ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; + +/** + * IBM Db2 Schema Manager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class DB2SchemaManager extends AbstractSchemaManager +{ + /** + * Return a list of all tables in the current database + * + * Apparently creator is the schema not the user who created it: + * {@link http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.sqlref/db2z_sysibmsystablestable.htm} + * + * @return array + */ + public function listTableNames() + { + $sql = $this->_platform->getListTablesSQL(); + $sql .= " AND CREATOR = UPPER('".$this->_conn->getUsername()."')"; + + $tables = $this->_conn->fetchAll($sql); + + return $this->_getPortableTablesList($tables); + } + + + /** + * Get Table Column Definition + * + * @param array $tableColumn + * @return Column + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, \CASE_LOWER); + + $length = null; + $fixed = null; + $unsigned = false; + $scale = false; + $precision = false; + + $type = $this->_platform->getDoctrineTypeMapping($tableColumn['typename']); + + switch (strtolower($tableColumn['typename'])) { + case 'varchar': + $length = $tableColumn['length']; + $fixed = false; + break; + case 'character': + $length = $tableColumn['length']; + $fixed = true; + break; + case 'clob': + $length = $tableColumn['length']; + break; + case 'decimal': + case 'double': + case 'real': + $scale = $tableColumn['scale']; + $precision = $tableColumn['length']; + break; + } + + $options = array( + 'length' => $length, + 'unsigned' => (bool)$unsigned, + 'fixed' => (bool)$fixed, + 'default' => ($tableColumn['default'] == "NULL") ? null : $tableColumn['default'], + 'notnull' => (bool) ($tableColumn['nulls'] == 'N'), + 'scale' => null, + 'precision' => null, + 'platformOptions' => array(), + ); + + if ($scale !== null && $precision !== null) { + $options['scale'] = $scale; + $options['precision'] = $precision; + } + + return new Column($tableColumn['colname'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + protected function _getPortableTablesList($tables) + { + $tableNames = array(); + foreach ($tables AS $tableRow) { + $tableRow = array_change_key_case($tableRow, \CASE_LOWER); + $tableNames[] = $tableRow['name']; + } + return $tableNames; + } + + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $eventManager = $this->_platform->getEventManager(); + + $tableIndexRows = array(); + $indexes = array(); + foreach($tableIndexes AS $indexKey => $data) { + $data = array_change_key_case($data, \CASE_LOWER); + $unique = ($data['uniquerule'] == "D") ? false : true; + $primary = ($data['uniquerule'] == "P"); + + $indexName = strtolower($data['name']); + if ($primary) { + $keyName = 'primary'; + } else { + $keyName = $indexName; + } + + $data = array( + 'name' => $indexName, + 'columns' => explode("+", ltrim($data['colnames'], '+')), + 'unique' => $unique, + 'primary' => $primary + ); + + $index = null; + $defaultPrevented = false; + + if (null !== $eventManager && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) { + $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn); + $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs); + + $defaultPrevented = $eventArgs->isDefaultPrevented(); + $index = $eventArgs->getIndex(); + } + + if (!$defaultPrevented) { + $index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary']); + } + + if ($index) { + $indexes[$indexKey] = $index; + } + } + + return $indexes; + } + + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $tableForeignKey = array_change_key_case($tableForeignKey, CASE_LOWER); + + $tableForeignKey['deleterule'] = $this->_getPortableForeignKeyRuleDef($tableForeignKey['deleterule']); + $tableForeignKey['updaterule'] = $this->_getPortableForeignKeyRuleDef($tableForeignKey['updaterule']); + + return new ForeignKeyConstraint( + array_map('trim', (array)$tableForeignKey['fkcolnames']), + $tableForeignKey['reftbname'], + array_map('trim', (array)$tableForeignKey['pkcolnames']), + $tableForeignKey['relname'], + array( + 'onUpdate' => $tableForeignKey['updaterule'], + 'onDelete' => $tableForeignKey['deleterule'], + ) + ); + } + + protected function _getPortableForeignKeyRuleDef($def) + { + if ($def == "C") { + return "CASCADE"; + } else if ($def == "N") { + return "SET NULL"; + } + return null; + } + + protected function _getPortableViewDefinition($view) + { + $view = array_change_key_case($view, \CASE_LOWER); + // sadly this still segfaults on PDO_IBM, see http://pecl.php.net/bugs/bug.php?id=17199 + //$view['text'] = (is_resource($view['text']) ? stream_get_contents($view['text']) : $view['text']); + if (!is_resource($view['text'])) { + $pos = strpos($view['text'], ' AS '); + $sql = substr($view['text'], $pos+4); + } else { + $sql = ''; + } + + return new View($view['name'], $sql); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php new file mode 100644 index 0000000..398c727 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php @@ -0,0 +1,164 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; + +class ForeignKeyConstraint extends AbstractAsset implements Constraint +{ + /** + * @var Table + */ + protected $_localTable; + + /** + * @var array + */ + protected $_localColumnNames; + + /** + * @var string + */ + protected $_foreignTableName; + + /** + * @var array + */ + protected $_foreignColumnNames; + + /** + * @var string + */ + protected $_cascade = ''; + + /** + * @var array + */ + protected $_options; + + /** + * + * @param array $localColumnNames + * @param string $foreignTableName + * @param array $foreignColumnNames + * @param string $cascade + * @param string|null $name + */ + public function __construct(array $localColumnNames, $foreignTableName, array $foreignColumnNames, $name=null, array $options=array()) + { + $this->_setName($name); + $this->_localColumnNames = $localColumnNames; + $this->_foreignTableName = $foreignTableName; + $this->_foreignColumnNames = $foreignColumnNames; + $this->_options = $options; + } + + /** + * @return string + */ + public function getLocalTableName() + { + return $this->_localTable->getName(); + } + + /** + * @param Table $table + */ + public function setLocalTable(Table $table) + { + $this->_localTable = $table; + } + + /** + * @return array + */ + public function getLocalColumns() + { + return $this->_localColumnNames; + } + + public function getColumns() + { + return $this->_localColumnNames; + } + + /** + * @return string + */ + public function getForeignTableName() + { + return $this->_foreignTableName; + } + + /** + * @return array + */ + public function getForeignColumns() + { + return $this->_foreignColumnNames; + } + + public function hasOption($name) + { + return isset($this->_options[$name]); + } + + public function getOption($name) + { + return $this->_options[$name]; + } + + /** + * Foreign Key onUpdate status + * + * @return string|null + */ + public function onUpdate() + { + return $this->_onEvent('onUpdate'); + } + + /** + * Foreign Key onDelete status + * + * @return string|null + */ + public function onDelete() + { + return $this->_onEvent('onDelete'); + } + + /** + * @param string $event + * @return string|null + */ + private function _onEvent($event) + { + if (isset($this->_options[$event])) { + $onEvent = strtoupper($this->_options[$event]); + if (!in_array($onEvent, array('NO ACTION', 'RESTRICT'))) { + return $onEvent; + } + } + return false; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php new file mode 100644 index 0000000..5a8e6c3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php @@ -0,0 +1,188 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; + +class Index extends AbstractAsset implements Constraint +{ + /** + * @var array + */ + protected $_columns; + + /** + * @var bool + */ + protected $_isUnique = false; + + /** + * @var bool + */ + protected $_isPrimary = false; + + /** + * @param string $indexName + * @param array $column + * @param bool $isUnique + * @param bool $isPrimary + */ + public function __construct($indexName, array $columns, $isUnique=false, $isPrimary=false) + { + $isUnique = ($isPrimary)?true:$isUnique; + + $this->_setName($indexName); + $this->_isUnique = $isUnique; + $this->_isPrimary = $isPrimary; + + foreach($columns AS $column) { + $this->_addColumn($column); + } + } + + /** + * @param string $column + */ + protected function _addColumn($column) + { + if(is_string($column)) { + $this->_columns[] = $column; + } else { + throw new \InvalidArgumentException("Expecting a string as Index Column"); + } + } + + /** + * @return array + */ + public function getColumns() + { + return $this->_columns; + } + + /** + * Is the index neither unique nor primary key? + * + * @return bool + */ + public function isSimpleIndex() + { + return !$this->_isPrimary && !$this->_isUnique; + } + + /** + * @return bool + */ + public function isUnique() + { + return $this->_isUnique; + } + + /** + * @return bool + */ + public function isPrimary() + { + return $this->_isPrimary; + } + + /** + * @param string $columnName + * @param int $pos + * @return bool + */ + public function hasColumnAtPosition($columnName, $pos=0) + { + $columnName = strtolower($columnName); + $indexColumns = \array_map('strtolower', $this->getColumns()); + return \array_search($columnName, $indexColumns) === $pos; + } + + /** + * Check if this index exactly spans the given column names in the correct order. + * + * @param array $columnNames + * @return boolean + */ + public function spansColumns(array $columnNames) + { + $sameColumns = true; + for ($i = 0; $i < count($this->_columns); $i++) { + if (!isset($columnNames[$i]) || strtolower($this->_columns[$i]) != strtolower($columnNames[$i])) { + $sameColumns = false; + } + } + return $sameColumns; + } + + /** + * Check if the other index already fullfills all the indexing and constraint needs of the current one. + * + * @param Index $other + * @return bool + */ + public function isFullfilledBy(Index $other) + { + // allow the other index to be equally large only. It being larger is an option + // but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo) + if (count($other->getColumns()) != count($this->getColumns())) { + return false; + } + + // Check if columns are the same, and even in the same order + $sameColumns = $this->spansColumns($other->getColumns()); + + if ($sameColumns) { + if (!$this->isUnique() && !$this->isPrimary()) { + // this is a special case: If the current key is neither primary or unique, any uniqe or + // primary key will always have the same effect for the index and there cannot be any constraint + // overlaps. This means a primary or unique index can always fullfill the requirements of just an + // index that has no constraints. + return true; + } else if ($other->isPrimary() != $this->isPrimary()) { + return false; + } else if ($other->isUnique() != $this->isUnique()) { + return false; + } + return true; + } + return false; + } + + /** + * Detect if the other index is a non-unique, non primary index that can be overwritten by this one. + * + * @param Index $other + * @return bool + */ + public function overrules(Index $other) + { + if ($other->isPrimary()) { + return false; + } else if ($this->isSimpleIndex() && $other->isUnique()) { + return false; + } + + if ($this->spansColumns($other->getColumns()) && ($this->isPrimary() || $this->isUnique())) { + return true; + } + return false; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php new file mode 100644 index 0000000..5796f94 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -0,0 +1,211 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Schema manager for the MySql RDBMS. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Roman Borschel + * @author Benjamin Eberlei + * @version $Revision$ + * @since 2.0 + */ +class MySqlSchemaManager extends AbstractSchemaManager +{ + protected function _getPortableViewDefinition($view) + { + return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); + } + + protected function _getPortableTableDefinition($table) + { + return array_shift($table); + } + + protected function _getPortableUserDefinition($user) + { + return array( + 'user' => $user['User'], + 'password' => $user['Password'], + ); + } + + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + foreach($tableIndexes AS $k => $v) { + $v = array_change_key_case($v, CASE_LOWER); + if($v['key_name'] == 'PRIMARY') { + $v['primary'] = true; + } else { + $v['primary'] = false; + } + $tableIndexes[$k] = $v; + } + + return parent::_getPortableTableIndexesList($tableIndexes, $tableName); + } + + protected function _getPortableSequenceDefinition($sequence) + { + return end($sequence); + } + + protected function _getPortableDatabaseDefinition($database) + { + return $database['Database']; + } + + /** + * Gets a portable column definition. + * + * The database type is mapped to a corresponding Doctrine mapping type. + * + * @param $tableColumn + * @return array + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); + + $dbType = strtolower($tableColumn['type']); + $dbType = strtok($dbType, '(), '); + if (isset($tableColumn['length'])) { + $length = $tableColumn['length']; + $decimal = ''; + } else { + $length = strtok('(), '); + $decimal = strtok('(), ') ? strtok('(), '):null; + } + $type = array(); + $unsigned = $fixed = null; + + if ( ! isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $scale = null; + $precision = null; + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + + switch ($dbType) { + case 'char': + $fixed = true; + break; + case 'float': + case 'double': + case 'real': + case 'numeric': + case 'decimal': + if(preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['type'], $match)) { + $precision = $match[1]; + $scale = $match[2]; + $length = null; + } + break; + case 'tinyint': + case 'smallint': + case 'mediumint': + case 'int': + case 'integer': + case 'bigint': + case 'tinyblob': + case 'mediumblob': + case 'longblob': + case 'blob': + case 'year': + $length = null; + break; + } + + $length = ((int) $length == 0) ? null : (int) $length; + $def = array( + 'type' => $type, + 'length' => $length, + 'unsigned' => (bool) $unsigned, + 'fixed' => (bool) $fixed + ); + + $options = array( + 'length' => $length, + 'unsigned' => (bool)$unsigned, + 'fixed' => (bool)$fixed, + 'default' => isset($tableColumn['default']) ? $tableColumn['default'] : null, + 'notnull' => (bool) ($tableColumn['null'] != 'YES'), + 'scale' => null, + 'precision' => null, + 'autoincrement' => (bool) (strpos($tableColumn['extra'], 'auto_increment') !== false), + 'comment' => (isset($tableColumn['comment'])) ? $tableColumn['comment'] : null + ); + + if ($scale !== null && $precision !== null) { + $options['scale'] = $scale; + $options['precision'] = $precision; + } + + return new Column($tableColumn['field'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $key => $value) { + $value = array_change_key_case($value, CASE_LOWER); + if (!isset($list[$value['constraint_name']])) { + if (!isset($value['delete_rule']) || $value['delete_rule'] == "RESTRICT") { + $value['delete_rule'] = null; + } + if (!isset($value['update_rule']) || $value['update_rule'] == "RESTRICT") { + $value['update_rule'] = null; + } + + $list[$value['constraint_name']] = array( + 'name' => $value['constraint_name'], + 'local' => array(), + 'foreign' => array(), + 'foreignTable' => $value['referenced_table_name'], + 'onDelete' => $value['delete_rule'], + 'onUpdate' => $value['update_rule'], + ); + } + $list[$value['constraint_name']]['local'][] = $value['column_name']; + $list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name']; + } + + $result = array(); + foreach($list AS $constraint) { + $result[] = new ForeignKeyConstraint( + array_values($constraint['local']), $constraint['foreignTable'], + array_values($constraint['foreign']), $constraint['name'], + array( + 'onDelete' => $constraint['onDelete'], + 'onUpdate' => $constraint['onUpdate'], + ) + ); + } + + return $result; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php new file mode 100644 index 0000000..3bbefb6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php @@ -0,0 +1,286 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Oracle Schema Manager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @version $Revision$ + * @since 2.0 + */ +class OracleSchemaManager extends AbstractSchemaManager +{ + protected function _getPortableViewDefinition($view) + { + $view = \array_change_key_case($view, CASE_LOWER); + + return new View($view['view_name'], $view['text']); + } + + protected function _getPortableUserDefinition($user) + { + $user = \array_change_key_case($user, CASE_LOWER); + + return array( + 'user' => $user['username'], + ); + } + + protected function _getPortableTableDefinition($table) + { + $table = \array_change_key_case($table, CASE_LOWER); + + return $table['table_name']; + } + + /** + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + * @param array $tableIndexes + * @param string $tableName + * @return array + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $indexBuffer = array(); + foreach ( $tableIndexes as $tableIndex ) { + $tableIndex = \array_change_key_case($tableIndex, CASE_LOWER); + + $keyName = strtolower($tableIndex['name']); + + if ( strtolower($tableIndex['is_primary']) == "p" ) { + $keyName = 'primary'; + $buffer['primary'] = true; + $buffer['non_unique'] = false; + } else { + $buffer['primary'] = false; + $buffer['non_unique'] = ( $tableIndex['is_unique'] == 0 ) ? true : false; + } + $buffer['key_name'] = $keyName; + $buffer['column_name'] = $tableIndex['column_name']; + $indexBuffer[] = $buffer; + } + return parent::_getPortableTableIndexesList($indexBuffer, $tableName); + } + + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = \array_change_key_case($tableColumn, CASE_LOWER); + + $dbType = strtolower($tableColumn['data_type']); + if(strpos($dbType, "timestamp(") === 0) { + if (strpos($dbType, "WITH TIME ZONE")) { + $dbType = "timestamptz"; + } else { + $dbType = "timestamp"; + } + } + + $type = array(); + $length = $unsigned = $fixed = null; + if ( ! empty($tableColumn['data_length'])) { + $length = $tableColumn['data_length']; + } + + if ( ! isset($tableColumn['column_name'])) { + $tableColumn['column_name'] = ''; + } + + if (stripos($tableColumn['data_default'], 'NULL') !== null) { + $tableColumn['data_default'] = null; + } + + $precision = null; + $scale = null; + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comments'], $type); + $tableColumn['comments'] = $this->removeDoctrineTypeFromComment($tableColumn['comments'], $type); + + switch ($dbType) { + case 'number': + if ($tableColumn['data_precision'] == 20 && $tableColumn['data_scale'] == 0) { + $precision = 20; + $scale = 0; + $type = 'bigint'; + } elseif ($tableColumn['data_precision'] == 5 && $tableColumn['data_scale'] == 0) { + $type = 'smallint'; + $precision = 5; + $scale = 0; + } elseif ($tableColumn['data_precision'] == 1 && $tableColumn['data_scale'] == 0) { + $precision = 1; + $scale = 0; + $type = 'boolean'; + } elseif ($tableColumn['data_scale'] > 0) { + $precision = $tableColumn['data_precision']; + $scale = $tableColumn['data_scale']; + $type = 'decimal'; + } + $length = null; + break; + case 'pls_integer': + case 'binary_integer': + $length = null; + break; + case 'varchar': + case 'varchar2': + case 'nvarchar2': + $length = $tableColumn['char_length']; + $fixed = false; + break; + case 'char': + case 'nchar': + $length = $tableColumn['char_length']; + $fixed = true; + break; + case 'date': + case 'timestamp': + $length = null; + break; + case 'float': + $precision = $tableColumn['data_precision']; + $scale = $tableColumn['data_scale']; + $length = null; + break; + case 'clob': + case 'nclob': + $length = null; + break; + case 'blob': + case 'raw': + case 'long raw': + case 'bfile': + $length = null; + break; + case 'rowid': + case 'urowid': + default: + $length = null; + } + + $options = array( + 'notnull' => (bool) ($tableColumn['nullable'] === 'N'), + 'fixed' => (bool) $fixed, + 'unsigned' => (bool) $unsigned, + 'default' => $tableColumn['data_default'], + 'length' => $length, + 'precision' => $precision, + 'scale' => $scale, + 'comment' => (isset($tableColumn['comments'])) ? $tableColumn['comments'] : null, + 'platformDetails' => array(), + ); + + return new Column($tableColumn['column_name'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $key => $value) { + $value = \array_change_key_case($value, CASE_LOWER); + if (!isset($list[$value['constraint_name']])) { + if ($value['delete_rule'] == "NO ACTION") { + $value['delete_rule'] = null; + } + + $list[$value['constraint_name']] = array( + 'name' => $value['constraint_name'], + 'local' => array(), + 'foreign' => array(), + 'foreignTable' => $value['references_table'], + 'onDelete' => $value['delete_rule'], + ); + } + $list[$value['constraint_name']]['local'][$value['position']] = $value['local_column']; + $list[$value['constraint_name']]['foreign'][$value['position']] = $value['foreign_column']; + } + + $result = array(); + foreach($list AS $constraint) { + $result[] = new ForeignKeyConstraint( + array_values($constraint['local']), $constraint['foreignTable'], + array_values($constraint['foreign']), $constraint['name'], + array('onDelete' => $constraint['onDelete']) + ); + } + + return $result; + } + + protected function _getPortableSequenceDefinition($sequence) + { + $sequence = \array_change_key_case($sequence, CASE_LOWER); + return new Sequence($sequence['sequence_name'], $sequence['increment_by'], $sequence['min_value']); + } + + protected function _getPortableFunctionDefinition($function) + { + $function = \array_change_key_case($function, CASE_LOWER); + return $function['name']; + } + + protected function _getPortableDatabaseDefinition($database) + { + $database = \array_change_key_case($database, CASE_LOWER); + return $database['username']; + } + + public function createDatabase($database = null) + { + if (is_null($database)) { + $database = $this->_conn->getDatabase(); + } + + $params = $this->_conn->getParams(); + $username = $database; + $password = $params['password']; + + $query = 'CREATE USER ' . $username . ' IDENTIFIED BY ' . $password; + $result = $this->_conn->executeUpdate($query); + + $query = 'GRANT CREATE SESSION, CREATE TABLE, UNLIMITED TABLESPACE, CREATE SEQUENCE, CREATE TRIGGER TO ' . $username; + $result = $this->_conn->executeUpdate($query); + + return true; + } + + public function dropAutoincrement($table) + { + $sql = $this->_platform->getDropAutoincrementSql($table); + foreach ($sql as $query) { + $this->_conn->executeUpdate($query); + } + + return true; + } + + public function dropTable($name) + { + $this->dropAutoincrement($name); + + return parent::dropTable($name); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php new file mode 100644 index 0000000..87b5301 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php @@ -0,0 +1,359 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * PostgreSQL Schema Manager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @since 2.0 + */ +class PostgreSqlSchemaManager extends AbstractSchemaManager +{ + /** + * @var array + */ + private $existingSchemaPaths; + + /** + * Get all the existing schema names. + * + * @return array + */ + public function getSchemaNames() + { + $rows = $this->_conn->fetchAll("SELECT nspname as schema_name FROM pg_namespace WHERE nspname !~ '^pg_.*' and nspname != 'information_schema'"); + return array_map(function($v) { return $v['schema_name']; }, $rows); + } + + /** + * Return an array of schema search paths + * + * This is a PostgreSQL only function. + * + * @return array + */ + public function getSchemaSearchPaths() + { + $params = $this->_conn->getParams(); + $schema = explode(",", $this->_conn->fetchColumn('SHOW search_path')); + if (isset($params['user'])) { + $schema = str_replace('"$user"', $params['user'], $schema); + } + return $schema; + } + + /** + * Get names of all existing schemas in the current users search path. + * + * This is a PostgreSQL only function. + * + * @return array + */ + public function getExistingSchemaSearchPaths() + { + if ($this->existingSchemaPaths === null) { + $this->determineExistingSchemaSearchPaths(); + } + return $this->existingSchemaPaths; + } + + /** + * Use this to set or reset the order of the existing schemas in the current search path of the user + * + * This is a PostgreSQL only function. + * + * @return type + */ + public function determineExistingSchemaSearchPaths() + { + $names = $this->getSchemaNames(); + $paths = $this->getSchemaSearchPaths(); + + $this->existingSchemaPaths = array_filter($paths, function ($v) use ($names) { + return in_array($v, $names); + }); + } + + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $onUpdate = null; + $onDelete = null; + + if (preg_match('(ON UPDATE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) { + $onUpdate = $match[1]; + } + if (preg_match('(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) { + $onDelete = $match[1]; + } + + if (preg_match('/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)/', $tableForeignKey['condef'], $values)) { + // PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get + // the idea to trim them here. + $localColumns = array_map('trim', explode(",", $values[1])); + $foreignColumns = array_map('trim', explode(",", $values[3])); + $foreignTable = $values[2]; + } + + return new ForeignKeyConstraint( + $localColumns, $foreignTable, $foreignColumns, $tableForeignKey['conname'], + array('onUpdate' => $onUpdate, 'onDelete' => $onDelete) + ); + } + + public function dropDatabase($database) + { + $params = $this->_conn->getParams(); + $params["dbname"] = "postgres"; + $tmpPlatform = $this->_platform; + $tmpConn = $this->_conn; + + $this->_conn = \Doctrine\DBAL\DriverManager::getConnection($params); + $this->_platform = $this->_conn->getDatabasePlatform(); + + parent::dropDatabase($database); + + $this->_platform = $tmpPlatform; + $this->_conn = $tmpConn; + } + + public function createDatabase($database) + { + $params = $this->_conn->getParams(); + $params["dbname"] = "postgres"; + $tmpPlatform = $this->_platform; + $tmpConn = $this->_conn; + + $this->_conn = \Doctrine\DBAL\DriverManager::getConnection($params); + $this->_platform = $this->_conn->getDatabasePlatform(); + + parent::createDatabase($database); + + $this->_platform = $tmpPlatform; + $this->_conn = $tmpConn; + } + + protected function _getPortableTriggerDefinition($trigger) + { + return $trigger['trigger_name']; + } + + protected function _getPortableViewDefinition($view) + { + return new View($view['viewname'], $view['definition']); + } + + protected function _getPortableUserDefinition($user) + { + return array( + 'user' => $user['usename'], + 'password' => $user['passwd'] + ); + } + + protected function _getPortableTableDefinition($table) + { + $schemas = $this->getExistingSchemaSearchPaths(); + $firstSchema = array_shift($schemas); + + if ($table['schema_name'] == $firstSchema) { + return $table['table_name']; + } else { + return $table['schema_name'] . "." . $table['table_name']; + } + } + + /** + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + * @param array $tableIndexes + * @param string $tableName + * @return array + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $buffer = array(); + foreach ($tableIndexes AS $row) { + $colNumbers = explode(' ', $row['indkey']); + $colNumbersSql = 'IN (' . join(' ,', $colNumbers) . ' )'; + $columnNameSql = "SELECT attnum, attname FROM pg_attribute + WHERE attrelid={$row['indrelid']} AND attnum $colNumbersSql ORDER BY attnum ASC;"; + + $stmt = $this->_conn->executeQuery($columnNameSql); + $indexColumns = $stmt->fetchAll(); + + // required for getting the order of the columns right. + foreach ($colNumbers AS $colNum) { + foreach ($indexColumns as $colRow) { + if ($colNum == $colRow['attnum']) { + $buffer[] = array( + 'key_name' => $row['relname'], + 'column_name' => trim($colRow['attname']), + 'non_unique' => !$row['indisunique'], + 'primary' => $row['indisprimary'] + ); + } + } + } + } + return parent::_getPortableTableIndexesList($buffer, $tableName); + } + + protected function _getPortableDatabaseDefinition($database) + { + return $database['datname']; + } + + protected function _getPortableSequenceDefinition($sequence) + { + if ($sequence['schemaname'] != 'public') { + $sequenceName = $sequence['schemaname'] . "." . $sequence['relname']; + } else { + $sequenceName = $sequence['relname']; + } + + $data = $this->_conn->fetchAll('SELECT min_value, increment_by FROM ' . $sequenceName); + return new Sequence($sequenceName, $data[0]['increment_by'], $data[0]['min_value']); + } + + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); + + if (strtolower($tableColumn['type']) === 'varchar') { + // get length from varchar definition + $length = preg_replace('~.*\(([0-9]*)\).*~', '$1', $tableColumn['complete_type']); + $tableColumn['length'] = $length; + } + + $matches = array(); + + $autoincrement = false; + if (preg_match("/^nextval\('(.*)'(::.*)?\)$/", $tableColumn['default'], $matches)) { + $tableColumn['sequence'] = $matches[1]; + $tableColumn['default'] = null; + $autoincrement = true; + } + + if (stripos($tableColumn['default'], 'NULL') === 0) { + $tableColumn['default'] = null; + } + + $length = (isset($tableColumn['length'])) ? $tableColumn['length'] : null; + if ($length == '-1' && isset($tableColumn['atttypmod'])) { + $length = $tableColumn['atttypmod'] - 4; + } + if ((int) $length <= 0) { + $length = null; + } + $fixed = null; + + if (!isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $precision = null; + $scale = null; + + $dbType = strtolower($tableColumn['type']); + if (strlen($tableColumn['domain_type']) && !$this->_platform->hasDoctrineTypeMappingFor($tableColumn['type'])) { + $dbType = strtolower($tableColumn['domain_type']); + $tableColumn['complete_type'] = $tableColumn['domain_complete_type']; + } + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + + switch ($dbType) { + case 'smallint': + case 'int2': + $length = null; + break; + case 'int': + case 'int4': + case 'integer': + $length = null; + break; + case 'bigint': + case 'int8': + $length = null; + break; + case 'bool': + case 'boolean': + $length = null; + break; + case 'text': + $fixed = false; + break; + case 'varchar': + case 'interval': + case '_varchar': + $fixed = false; + break; + case 'char': + case 'bpchar': + $fixed = true; + break; + case 'float': + case 'float4': + case 'float8': + case 'double': + case 'double precision': + case 'real': + case 'decimal': + case 'money': + case 'numeric': + if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['complete_type'], $match)) { + $precision = $match[1]; + $scale = $match[2]; + $length = null; + } + break; + case 'year': + $length = null; + break; + } + + if ($tableColumn['default'] && preg_match("('([^']+)'::)", $tableColumn['default'], $match)) { + $tableColumn['default'] = $match[1]; + } + + $options = array( + 'length' => $length, + 'notnull' => (bool) $tableColumn['isnotnull'], + 'default' => $tableColumn['default'], + 'primary' => (bool) ($tableColumn['pri'] == 't'), + 'precision' => $precision, + 'scale' => $scale, + 'fixed' => $fixed, + 'unsigned' => false, + 'autoincrement' => $autoincrement, + 'comment' => $tableColumn['comment'], + ); + + return new Column($tableColumn['field'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php new file mode 100644 index 0000000..0a2de79 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php @@ -0,0 +1,247 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; +use Doctrine\DBAL\Events; + +/** + * SQL Server Schema Manager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Juozas Kaziukenas + * @since 2.0 + */ +class SQLServerSchemaManager extends AbstractSchemaManager +{ + /** + * @override + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $dbType = strtolower($tableColumn['TYPE_NAME']); + + $autoincrement = false; + if (stripos($dbType, 'identity')) { + $dbType = trim(str_ireplace('identity', '', $dbType)); + $autoincrement = true; + } + + $type = array(); + $unsigned = $fixed = null; + + if (!isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $default = $tableColumn['COLUMN_DEF']; + + while ($default != ($default2 = preg_replace("/^\((.*)\)$/", '$1', $default))) { + $default = trim($default2, "'"); + } + + $length = (int) $tableColumn['LENGTH']; + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + switch ($type) { + case 'char': + if ($tableColumn['LENGTH'] == '1') { + $type = 'boolean'; + if (preg_match('/^(is|has)/', $tableColumn['name'])) { + $type = array_reverse($type); + } + } + $fixed = true; + break; + case 'text': + $fixed = false; + break; + } + switch ($dbType) { + case 'nchar': + case 'nvarchar': + case 'ntext': + // Unicode data requires 2 bytes per character + $length = $length / 2; + break; + } + + $options = array( + 'length' => ($length == 0 || !in_array($type, array('text', 'string'))) ? null : $length, + 'unsigned' => (bool) $unsigned, + 'fixed' => (bool) $fixed, + 'default' => $default !== 'NULL' ? $default : null, + 'notnull' => (bool) ($tableColumn['IS_NULLABLE'] != 'YES'), + 'scale' => $tableColumn['SCALE'], + 'precision' => $tableColumn['PRECISION'], + 'autoincrement' => $autoincrement, + ); + + return new Column($tableColumn['COLUMN_NAME'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + /** + * @override + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName=null) + { + $result = array(); + foreach ($tableIndexRows AS $tableIndex) { + $indexName = $keyName = $tableIndex['index_name']; + if (strpos($tableIndex['index_description'], 'primary key') !== false) { + $keyName = 'primary'; + } + $keyName = strtolower($keyName); + + $result[$keyName] = array( + 'name' => $indexName, + 'columns' => explode(', ', $tableIndex['index_keys']), + 'unique' => strpos($tableIndex['index_description'], 'unique') !== false, + 'primary' => strpos($tableIndex['index_description'], 'primary key') !== false, + ); + } + + $eventManager = $this->_platform->getEventManager(); + + $indexes = array(); + foreach ($result AS $indexKey => $data) { + $index = null; + $defaultPrevented = false; + + if (null !== $eventManager && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) { + $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn); + $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs); + + $defaultPrevented = $eventArgs->isDefaultPrevented(); + $index = $eventArgs->getIndex(); + } + + if (!$defaultPrevented) { + $index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary']); + } + + if ($index) { + $indexes[$indexKey] = $index; + } + } + + return $indexes; + } + + /** + * @override + */ + public function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return new ForeignKeyConstraint( + (array) $tableForeignKey['ColumnName'], + $tableForeignKey['ReferenceTableName'], + (array) $tableForeignKey['ReferenceColumnName'], + $tableForeignKey['ForeignKey'], + array( + 'onUpdate' => str_replace('_', ' ', $tableForeignKey['update_referential_action_desc']), + 'onDelete' => str_replace('_', ' ', $tableForeignKey['delete_referential_action_desc']), + ) + ); + } + + /** + * @override + */ + protected function _getPortableTableDefinition($table) + { + return $table['name']; + } + + /** + * @override + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['name']; + } + + /** + * @override + */ + protected function _getPortableViewDefinition($view) + { + // @todo + return new View($view['name'], null); + } + + /** + * List the indexes for a given table returning an array of Index instances. + * + * Keys of the portable indexes list are all lower-cased. + * + * @param string $table The name of the table + * @return Index[] $tableIndexes + */ + public function listTableIndexes($table) + { + $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); + + try { + $tableIndexes = $this->_conn->fetchAll($sql); + } catch(\PDOException $e) { + if ($e->getCode() == "IMSSP") { + return array(); + } else { + throw $e; + } + } + + return $this->_getPortableTableIndexesList($tableIndexes, $table); + } + + /** + * @override + */ + public function alterTable(TableDiff $tableDiff) + { + if(count($tableDiff->removedColumns) > 0) { + foreach($tableDiff->removedColumns as $col){ + $columnConstraintSql = $this->getColumnConstraintSQL($tableDiff->name, $col->getName()); + foreach ($this->_conn->fetchAll($columnConstraintSql) as $constraint) { + $this->_conn->exec("ALTER TABLE $tableDiff->name DROP CONSTRAINT " . $constraint['Name']); + } + } + } + + return parent::alterTable($tableDiff); + } + + /** + * This function retrieves the constraints for a given column. + */ + private function getColumnConstraintSQL($table, $column) + { + return "SELECT SysObjects.[Name] + FROM SysObjects INNER JOIN (SELECT [Name],[ID] FROM SysObjects WHERE XType = 'U') AS Tab + ON Tab.[ID] = Sysobjects.[Parent_Obj] + INNER JOIN sys.default_constraints DefCons ON DefCons.[object_id] = Sysobjects.[ID] + INNER JOIN SysColumns Col ON Col.[ColID] = DefCons.[parent_column_id] AND Col.[ID] = Tab.[ID] + WHERE Col.[Name] = " . $this->_conn->quote($column) ." AND Tab.[Name] = " . $this->_conn->quote($table) . " + ORDER BY Col.[Name]"; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php new file mode 100644 index 0000000..2c1b642 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php @@ -0,0 +1,367 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\Visitor; + +/** + * Object representation of a database schema + * + * Different vendors have very inconsistent naming with regard to the concept + * of a "schema". Doctrine understands a schema as the entity that conceptually + * wraps a set of database objects such as tables, sequences, indexes and + * foreign keys that belong to each other into a namespace. A Doctrine Schema + * has nothing to do with the "SCHEMA" defined as in PostgreSQL, it is more + * related to the concept of "DATABASE" that exists in MySQL and PostgreSQL. + * + * Every asset in the doctrine schema has a name. A name consists of either a + * namespace.local name pair or just a local unqualified name. + * + * The abstraction layer that covers a PostgreSQL schema is the namespace of an + * database object (asset). A schema can have a name, which will be used as + * default namespace for the unqualified database objects that are created in + * the schema. + * + * In the case of MySQL where cross-database queries are allowed this leads to + * databases being "misinterpreted" as namespaces. This is intentional, however + * the CREATE/DROP SQL visitors will just filter this queries and do not + * execute them. Only the queries for the currently connected database are + * executed. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Schema extends AbstractAsset +{ + /** + * @var array + */ + protected $_tables = array(); + + /** + * @var array + */ + protected $_sequences = array(); + + /** + * @var SchemaConfig + */ + protected $_schemaConfig = false; + + /** + * @param array $tables + * @param array $sequences + * @param array $views + * @param array $triggers + * @param SchemaConfig $schemaConfig + */ + public function __construct(array $tables=array(), array $sequences=array(), SchemaConfig $schemaConfig=null) + { + if ($schemaConfig == null) { + $schemaConfig = new SchemaConfig(); + } + $this->_schemaConfig = $schemaConfig; + $this->_setName($schemaConfig->getName() ?: 'public'); + + foreach ($tables AS $table) { + $this->_addTable($table); + } + foreach ($sequences AS $sequence) { + $this->_addSequence($sequence); + } + } + + /** + * @return bool + */ + public function hasExplicitForeignKeyIndexes() + { + return $this->_schemaConfig->hasExplicitForeignKeyIndexes(); + } + + /** + * @param Table $table + */ + protected function _addTable(Table $table) + { + $tableName = $table->getFullQualifiedName($this->getName()); + if(isset($this->_tables[$tableName])) { + throw SchemaException::tableAlreadyExists($tableName); + } + + $this->_tables[$tableName] = $table; + $table->setSchemaConfig($this->_schemaConfig); + } + + /** + * @param Sequence $sequence + */ + protected function _addSequence(Sequence $sequence) + { + $seqName = $sequence->getFullQualifiedName($this->getName()); + if (isset($this->_sequences[$seqName])) { + throw SchemaException::sequenceAlreadyExists($seqName); + } + $this->_sequences[$seqName] = $sequence; + } + + /** + * Get all tables of this schema. + * + * @return array + */ + public function getTables() + { + return $this->_tables; + } + + /** + * @param string $tableName + * @return Table + */ + public function getTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + if (!isset($this->_tables[$tableName])) { + throw SchemaException::tableDoesNotExist($tableName); + } + + return $this->_tables[$tableName]; + } + + /** + * @return string + */ + private function getFullQualifiedAssetName($name) + { + if ($this->isQuoted($name)) { + $name = $this->trimQuotes($name); + } + if (strpos($name, ".") === false) { + $name = $this->getName() . "." . $name; + } + return strtolower($name); + } + + /** + * Does this schema have a table with the given name? + * + * @param string $tableName + * @return Schema + */ + public function hasTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + return isset($this->_tables[$tableName]); + } + + /** + * Get all table names, prefixed with a schema name, even the default one + * if present. + * + * @return array + */ + public function getTableNames() + { + return array_keys($this->_tables); + } + + public function hasSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + return isset($this->_sequences[$sequenceName]); + } + + /** + * @throws SchemaException + * @param string $sequenceName + * @return Doctrine\DBAL\Schema\Sequence + */ + public function getSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + if(!$this->hasSequence($sequenceName)) { + throw SchemaException::sequenceDoesNotExist($sequenceName); + } + return $this->_sequences[$sequenceName]; + } + + /** + * @return Doctrine\DBAL\Schema\Sequence[] + */ + public function getSequences() + { + return $this->_sequences; + } + + /** + * Create a new table + * + * @param string $tableName + * @return Table + */ + public function createTable($tableName) + { + $table = new Table($tableName); + $this->_addTable($table); + return $table; + } + + /** + * Rename a table + * + * @param string $oldTableName + * @param string $newTableName + * @return Schema + */ + public function renameTable($oldTableName, $newTableName) + { + $table = $this->getTable($oldTableName); + $table->_setName($newTableName); + + $this->dropTable($oldTableName); + $this->_addTable($table); + return $this; + } + + /** + * Drop a table from the schema. + * + * @param string $tableName + * @return Schema + */ + public function dropTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + $table = $this->getTable($tableName); + unset($this->_tables[$tableName]); + return $this; + } + + /** + * Create a new sequence + * + * @param string $sequenceName + * @param int $allocationSize + * @param int $initialValue + * @return Sequence + */ + public function createSequence($sequenceName, $allocationSize=1, $initialValue=1) + { + $seq = new Sequence($sequenceName, $allocationSize, $initialValue); + $this->_addSequence($seq); + return $seq; + } + + /** + * @param string $sequenceName + * @return Schema + */ + public function dropSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + unset($this->_sequences[$sequenceName]); + return $this; + } + + /** + * Return an array of necessary sql queries to create the schema on the given platform. + * + * @param AbstractPlatform $platform + * @return array + */ + public function toSql(\Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + $sqlCollector = new CreateSchemaSqlCollector($platform); + $this->visit($sqlCollector); + + return $sqlCollector->getQueries(); + } + + /** + * Return an array of necessary sql queries to drop the schema on the given platform. + * + * @param AbstractPlatform $platform + * @return array + */ + public function toDropSql(\Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + $dropSqlCollector = new DropSchemaSqlCollector($platform); + $this->visit($dropSqlCollector); + + return $dropSqlCollector->getQueries(); + } + + /** + * @param Schema $toSchema + * @param AbstractPlatform $platform + */ + public function getMigrateToSql(Schema $toSchema, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + $comparator = new Comparator(); + $schemaDiff = $comparator->compare($this, $toSchema); + return $schemaDiff->toSql($platform); + } + + /** + * @param Schema $fromSchema + * @param AbstractPlatform $platform + */ + public function getMigrateFromSql(Schema $fromSchema, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + $comparator = new Comparator(); + $schemaDiff = $comparator->compare($fromSchema, $this); + return $schemaDiff->toSql($platform); + } + + /** + * @param Visitor $visitor + */ + public function visit(Visitor $visitor) + { + $visitor->acceptSchema($this); + + foreach ($this->_tables AS $table) { + $table->visit($visitor); + } + foreach ($this->_sequences AS $sequence) { + $sequence->visit($visitor); + } + } + + /** + * Cloning a Schema triggers a deep clone of all related assets. + * + * @return void + */ + public function __clone() + { + foreach ($this->_tables AS $k => $table) { + $this->_tables[$k] = clone $table; + } + foreach ($this->_sequences AS $k => $sequence) { + $this->_sequences[$k] = clone $sequence; + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php new file mode 100644 index 0000000..d4961f4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Configuration for a Schema + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class SchemaConfig +{ + /** + * @var bool + */ + protected $_hasExplicitForeignKeyIndexes = false; + + /** + * @var int + */ + protected $_maxIdentifierLength = 63; + + /** + * @var string + */ + protected $_name; + + /** + * @return bool + */ + public function hasExplicitForeignKeyIndexes() + { + return $this->_hasExplicitForeignKeyIndexes; + } + + /** + * @param bool $flag + */ + public function setExplicitForeignKeyIndexes($flag) + { + $this->_hasExplicitForeignKeyIndexes = (bool)$flag; + } + + /** + * @param int $length + */ + public function setMaxIdentifierLength($length) + { + $this->_maxIdentifierLength = (int)$length; + } + + /** + * @return int + */ + public function getMaxIdentifierLength() + { + return $this->_maxIdentifierLength; + } + + /** + * Get default namespace of schema objects. + * + * @return string + */ + public function getName() + { + return $this->_name; + } + + /** + * set default namespace name of schema objects. + * + * @param _name the value to set. + */ + public function setName($name) + { + $this->_name = $name; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaDiff.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaDiff.php new file mode 100644 index 0000000..64033c1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaDiff.php @@ -0,0 +1,176 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use \Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Schema Diff + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class SchemaDiff +{ + /** + * All added tables + * + * @var array(string=>ezcDbSchemaTable) + */ + public $newTables = array(); + + /** + * All changed tables + * + * @var array(string=>ezcDbSchemaTableDiff) + */ + public $changedTables = array(); + + /** + * All removed tables + * + * @var array(string=>Table) + */ + public $removedTables = array(); + + /** + * @var array + */ + public $newSequences = array(); + + /** + * @var array + */ + public $changedSequences = array(); + + /** + * @var array + */ + public $removedSequences = array(); + + /** + * @var array + */ + public $orphanedForeignKeys = array(); + + /** + * Constructs an SchemaDiff object. + * + * @param array(string=>Table) $newTables + * @param array(string=>TableDiff) $changedTables + * @param array(string=>bool) $removedTables + */ + public function __construct($newTables = array(), $changedTables = array(), $removedTables = array()) + { + $this->newTables = $newTables; + $this->changedTables = $changedTables; + $this->removedTables = $removedTables; + } + + /** + * The to save sql mode ensures that the following things don't happen: + * + * 1. Tables are deleted + * 2. Sequences are deleted + * 3. Foreign Keys which reference tables that would otherwise be deleted. + * + * This way it is ensured that assets are deleted which might not be relevant to the metadata schema at all. + * + * @param AbstractPlatform $platform + * @return array + */ + public function toSaveSql(AbstractPlatform $platform) + { + return $this->_toSql($platform, true); + } + + /** + * @param AbstractPlatform $platform + * @return array + */ + public function toSql(AbstractPlatform $platform) + { + return $this->_toSql($platform, false); + } + + /** + * @param AbstractPlatform $platform + * @param bool $saveMode + * @return array + */ + protected function _toSql(AbstractPlatform $platform, $saveMode = false) + { + $sql = array(); + + if ($platform->supportsForeignKeyConstraints() && $saveMode == false) { + foreach ($this->orphanedForeignKeys AS $orphanedForeignKey) { + $sql[] = $platform->getDropForeignKeySQL($orphanedForeignKey, $orphanedForeignKey->getLocalTableName()); + } + } + + if ($platform->supportsSequences() == true) { + foreach ($this->changedSequences AS $sequence) { + $sql[] = $platform->getAlterSequenceSQL($sequence); + } + + if ($saveMode === false) { + foreach ($this->removedSequences AS $sequence) { + $sql[] = $platform->getDropSequenceSQL($sequence); + } + } + + foreach ($this->newSequences AS $sequence) { + $sql[] = $platform->getCreateSequenceSQL($sequence); + } + } + + $foreignKeySql = array(); + foreach ($this->newTables AS $table) { + $sql = array_merge( + $sql, + $platform->getCreateTableSQL($table, AbstractPlatform::CREATE_INDEXES) + ); + + if ($platform->supportsForeignKeyConstraints()) { + foreach ($table->getForeignKeys() AS $foreignKey) { + $foreignKeySql[] = $platform->getCreateForeignKeySQL($foreignKey, $table); + } + } + } + $sql = array_merge($sql, $foreignKeySql); + + if ($saveMode === false) { + foreach ($this->removedTables AS $table) { + $sql[] = $platform->getDropTableSQL($table); + } + } + + foreach ($this->changedTables AS $tableDiff) { + $sql = array_merge($sql, $platform->getAlterTableSQL($tableDiff)); + } + + return $sql; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php new file mode 100644 index 0000000..a8cb93d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php @@ -0,0 +1,126 @@ +getName()." requires a named foreign key, ". + "but the given foreign key from (".implode(", ", $foreignKey->getColumns()).") onto foreign table ". + "'".$foreignKey->getForeignTableName()."' (".implode(", ", $foreignKey->getForeignColumns()).") is currently ". + "unnamed." + ); + } + + static public function alterTableChangeNotSupported($changeName) { + return new self ("Alter table change not supported, given '$changeName'"); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php new file mode 100644 index 0000000..6344cc7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php @@ -0,0 +1,87 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; + +/** + * Sequence Structure + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class Sequence extends AbstractAsset +{ + /** + * @var int + */ + protected $_allocationSize = 1; + + /** + * @var int + */ + protected $_initialValue = 1; + + /** + * + * @param string $name + * @param int $allocationSize + * @param int $initialValue + */ + public function __construct($name, $allocationSize=1, $initialValue=1) + { + $this->_setName($name); + $this->_allocationSize = (is_numeric($allocationSize))?$allocationSize:1; + $this->_initialValue = (is_numeric($initialValue))?$initialValue:1; + } + + public function getAllocationSize() + { + return $this->_allocationSize; + } + + public function getInitialValue() + { + return $this->_initialValue; + } + + public function setAllocationSize($allocationSize) + { + $this->_allocationSize = (is_numeric($allocationSize))?$allocationSize:1; + } + + public function setInitialValue($initialValue) + { + $this->_initialValue = (is_numeric($initialValue))?$initialValue:1; + } + + /** + * @param Visitor $visitor + */ + public function visit(Visitor $visitor) + { + $visitor->acceptSequence($this); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php new file mode 100644 index 0000000..9e91299 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php @@ -0,0 +1,190 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * SqliteSchemaManager + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Jonathan H. Wage + * @version $Revision$ + * @since 2.0 + */ +class SqliteSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + * + * @override + */ + public function dropDatabase($database) + { + if (file_exists($database)) { + unlink($database); + } + } + + /** + * {@inheritdoc} + * + * @override + */ + public function createDatabase($database) + { + $params = $this->_conn->getParams(); + $driver = $params['driver']; + $options = array( + 'driver' => $driver, + 'path' => $database + ); + $conn = \Doctrine\DBAL\DriverManager::getConnection($options); + $conn->connect(); + $conn->close(); + } + + protected function _getPortableTableDefinition($table) + { + return $table['name']; + } + + /** + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + * @param array $tableIndexes + * @param string $tableName + * @return array + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $indexBuffer = array(); + + // fetch primary + $stmt = $this->_conn->executeQuery( "PRAGMA TABLE_INFO ('$tableName')" ); + $indexArray = $stmt->fetchAll(\PDO::FETCH_ASSOC); + foreach($indexArray AS $indexColumnRow) { + if($indexColumnRow['pk'] == "1") { + $indexBuffer[] = array( + 'key_name' => 'primary', + 'primary' => true, + 'non_unique' => false, + 'column_name' => $indexColumnRow['name'] + ); + } + } + + // fetch regular indexes + foreach($tableIndexes AS $tableIndex) { + // Ignore indexes with reserved names, e.g. autoindexes + if (strpos($tableIndex['name'], 'sqlite_') !== 0) { + $keyName = $tableIndex['name']; + $idx = array(); + $idx['key_name'] = $keyName; + $idx['primary'] = false; + $idx['non_unique'] = $tableIndex['unique']?false:true; + + $stmt = $this->_conn->executeQuery( "PRAGMA INDEX_INFO ( '{$keyName}' )" ); + $indexArray = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + foreach ( $indexArray as $indexColumnRow ) { + $idx['column_name'] = $indexColumnRow['name']; + $indexBuffer[] = $idx; + } + } + } + + return parent::_getPortableTableIndexesList($indexBuffer, $tableName); + } + + protected function _getPortableTableIndexDefinition($tableIndex) + { + return array( + 'name' => $tableIndex['name'], + 'unique' => (bool) $tableIndex['unique'] + ); + } + + protected function _getPortableTableColumnDefinition($tableColumn) + { + $e = explode('(', $tableColumn['type']); + $tableColumn['type'] = $e[0]; + if (isset($e[1])) { + $length = trim($e[1], ')'); + $tableColumn['length'] = $length; + } + + $dbType = strtolower($tableColumn['type']); + $length = isset($tableColumn['length']) ? $tableColumn['length'] : null; + $unsigned = (boolean) isset($tableColumn['unsigned']) ? $tableColumn['unsigned'] : false; + $fixed = false; + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $default = $tableColumn['dflt_value']; + if ($default == 'NULL') { + $default = null; + } + if ($default !== null) { + // SQLite returns strings wrapped in single quotes, so we need to strip them + $default = preg_replace("/^'(.*)'$/", '\1', $default); + } + $notnull = (bool) $tableColumn['notnull']; + + if ( ! isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $precision = null; + $scale = null; + + switch ($dbType) { + case 'char': + $fixed = true; + break; + case 'float': + case 'double': + case 'real': + case 'decimal': + case 'numeric': + if (isset($tableColumn['length'])) { + list($precision, $scale) = array_map('trim', explode(', ', $tableColumn['length'])); + } + $length = null; + break; + } + + $options = array( + 'length' => $length, + 'unsigned' => (bool) $unsigned, + 'fixed' => $fixed, + 'notnull' => $notnull, + 'default' => $default, + 'precision' => $precision, + 'scale' => $scale, + 'autoincrement' => false, + ); + + return new Column($tableColumn['name'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + protected function _getPortableViewDefinition($view) + { + return new View($view['name'], $view['sql']); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php new file mode 100644 index 0000000..b7e3d04 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php @@ -0,0 +1,645 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\DBALException; + +/** + * Object Representation of a table + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class Table extends AbstractAsset +{ + /** + * @var string + */ + protected $_name = null; + + /** + * @var array + */ + protected $_columns = array(); + + /** + * @var array + */ + protected $_indexes = array(); + + /** + * @var string + */ + protected $_primaryKeyName = false; + + /** + * @var array + */ + protected $_fkConstraints = array(); + + /** + * @var array + */ + protected $_options = array(); + + /** + * @var SchemaConfig + */ + protected $_schemaConfig = null; + + /** + * + * @param string $tableName + * @param array $columns + * @param array $indexes + * @param array $fkConstraints + * @param int $idGeneratorType + * @param array $options + */ + public function __construct($tableName, array $columns=array(), array $indexes=array(), array $fkConstraints=array(), $idGeneratorType = 0, array $options=array()) + { + if (strlen($tableName) == 0) { + throw DBALException::invalidTableName($tableName); + } + + $this->_setName($tableName); + $this->_idGeneratorType = $idGeneratorType; + + foreach ($columns AS $column) { + $this->_addColumn($column); + } + + foreach ($indexes AS $idx) { + $this->_addIndex($idx); + } + + foreach ($fkConstraints AS $constraint) { + $this->_addForeignKeyConstraint($constraint); + } + + $this->_options = $options; + } + + /** + * @param SchemaConfig $schemaConfig + */ + public function setSchemaConfig(SchemaConfig $schemaConfig) + { + $this->_schemaConfig = $schemaConfig; + } + + /** + * @return int + */ + protected function _getMaxIdentifierLength() + { + if ($this->_schemaConfig instanceof SchemaConfig) { + return $this->_schemaConfig->getMaxIdentifierLength(); + } else { + return 63; + } + } + + /** + * Set Primary Key + * + * @param array $columns + * @param string $indexName + * @return Table + */ + public function setPrimaryKey(array $columns, $indexName = false) + { + $primaryKey = $this->_createIndex($columns, $indexName ?: "primary", true, true); + + foreach ($columns AS $columnName) { + $column = $this->getColumn($columnName); + $column->setNotnull(true); + } + + return $primaryKey; + } + + /** + * @param array $columnNames + * @param string $indexName + * @return Table + */ + public function addIndex(array $columnNames, $indexName = null) + { + if($indexName == null) { + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $columnNames), "idx", $this->_getMaxIdentifierLength() + ); + } + + return $this->_createIndex($columnNames, $indexName, false, false); + } + + /** + * + * @param array $columnNames + * @param string $indexName + * @return Table + */ + public function addUniqueIndex(array $columnNames, $indexName = null) + { + if ($indexName === null) { + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $columnNames), "uniq", $this->_getMaxIdentifierLength() + ); + } + + return $this->_createIndex($columnNames, $indexName, true, false); + } + + /** + * Check if an index begins in the order of the given columns. + * + * @param array $columnsNames + * @return bool + */ + public function columnsAreIndexed(array $columnsNames) + { + foreach ($this->getIndexes() AS $index) { + /* @var $index Index */ + if ($index->spansColumns($columnsNames)) { + return true; + } + } + return false; + } + + /** + * + * @param array $columnNames + * @param string $indexName + * @param bool $isUnique + * @param bool $isPrimary + * @return Table + */ + private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary) + { + if (preg_match('(([^a-zA-Z0-9_]+))', $indexName)) { + throw SchemaException::indexNameInvalid($indexName); + } + + foreach ($columnNames AS $columnName => $indexColOptions) { + if (is_numeric($columnName) && is_string($indexColOptions)) { + $columnName = $indexColOptions; + } + + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + $this->_addIndex(new Index($indexName, $columnNames, $isUnique, $isPrimary)); + return $this; + } + + /** + * @param string $columnName + * @param string $columnType + * @param array $options + * @return Column + */ + public function addColumn($columnName, $typeName, array $options=array()) + { + $column = new Column($columnName, Type::getType($typeName), $options); + + $this->_addColumn($column); + return $column; + } + + /** + * Rename Column + * + * @param string $oldColumnName + * @param string $newColumnName + * @return Table + */ + public function renameColumn($oldColumnName, $newColumnName) + { + $column = $this->getColumn($oldColumnName); + $this->dropColumn($oldColumnName); + + $column->_setName($newColumnName); + return $this; + } + + /** + * Change Column Details + * + * @param string $columnName + * @param array $options + * @return Table + */ + public function changeColumn($columnName, array $options) + { + $column = $this->getColumn($columnName); + $column->setOptions($options); + return $this; + } + + /** + * Drop Column from Table + * + * @param string $columnName + * @return Table + */ + public function dropColumn($columnName) + { + $columnName = strtolower($columnName); + $column = $this->getColumn($columnName); + unset($this->_columns[$columnName]); + return $this; + } + + + /** + * Add a foreign key constraint + * + * Name is inferred from the local columns + * + * @param Table $foreignTable + * @param array $localColumns + * @param array $foreignColumns + * @param array $options + * @param string $constraintName + * @return Table + */ + public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array(), $constraintName = null) + { + $constraintName = $constraintName ?: $this->_generateIdentifierName(array_merge((array)$this->getName(), $localColumnNames), "fk", $this->_getMaxIdentifierLength()); + return $this->addNamedForeignKeyConstraint($constraintName, $foreignTable, $localColumnNames, $foreignColumnNames, $options); + } + + /** + * Add a foreign key constraint + * + * Name is to be generated by the database itsself. + * + * @deprecated Use {@link addForeignKeyConstraint} + * @param Table $foreignTable + * @param array $localColumns + * @param array $foreignColumns + * @param array $options + * @return Table + */ + public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array()) + { + return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options); + } + + /** + * Add a foreign key constraint with a given name + * + * @deprecated Use {@link addForeignKeyConstraint} + * @param string $name + * @param Table $foreignTable + * @param array $localColumns + * @param array $foreignColumns + * @param array $options + * @return Table + */ + public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array()) + { + if ($foreignTable instanceof Table) { + $foreignTableName = $foreignTable->getName(); + + foreach ($foreignColumnNames AS $columnName) { + if ( ! $foreignTable->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName()); + } + } + } else { + $foreignTableName = $foreignTable; + } + + foreach ($localColumnNames AS $columnName) { + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + + $constraint = new ForeignKeyConstraint( + $localColumnNames, $foreignTableName, $foreignColumnNames, $name, $options + ); + $this->_addForeignKeyConstraint($constraint); + + return $this; + } + + /** + * @param string $name + * @param string $value + * @return Table + */ + public function addOption($name, $value) + { + $this->_options[$name] = $value; + return $this; + } + + /** + * @param Column $column + */ + protected function _addColumn(Column $column) + { + $columnName = $column->getName(); + $columnName = strtolower($columnName); + + if (isset($this->_columns[$columnName])) { + throw SchemaException::columnAlreadyExists($this->getName(), $columnName); + } + + $this->_columns[$columnName] = $column; + } + + /** + * Add index to table + * + * @param Index $indexCandidate + * @return Table + */ + protected function _addIndex(Index $indexCandidate) + { + // check for duplicates + foreach ($this->_indexes AS $existingIndex) { + if ($indexCandidate->isFullfilledBy($existingIndex)) { + return $this; + } + } + + $indexName = $indexCandidate->getName(); + $indexName = strtolower($indexName); + + if (isset($this->_indexes[$indexName]) || ($this->_primaryKeyName != false && $indexCandidate->isPrimary())) { + throw SchemaException::indexAlreadyExists($indexName, $this->_name); + } + + // remove overruled indexes + foreach ($this->_indexes AS $idxKey => $existingIndex) { + if ($indexCandidate->overrules($existingIndex)) { + unset($this->_indexes[$idxKey]); + } + } + + if ($indexCandidate->isPrimary()) { + $this->_primaryKeyName = $indexName; + } + + $this->_indexes[$indexName] = $indexCandidate; + return $this; + } + + /** + * @param ForeignKeyConstraint $constraint + */ + protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) + { + $constraint->setLocalTable($this); + + if(strlen($constraint->getName())) { + $name = $constraint->getName(); + } else { + $name = $this->_generateIdentifierName( + array_merge((array)$this->getName(), $constraint->getLocalColumns()), "fk", $this->_getMaxIdentifierLength() + ); + } + $name = strtolower($name); + + $this->_fkConstraints[$name] = $constraint; + // add an explicit index on the foreign key columns. If there is already an index that fullfils this requirements drop the request. + // In the case of __construct calling this method during hydration from schema-details all the explicitly added indexes + // lead to duplicates. This creates compuation overhead in this case, however no duplicate indexes are ever added (based on columns). + $this->addIndex($constraint->getColumns()); + } + + /** + * Does Table have a foreign key constraint with the given name? + * * + * @param string $constraintName + * @return bool + */ + public function hasForeignKey($constraintName) + { + $constraintName = strtolower($constraintName); + return isset($this->_fkConstraints[$constraintName]); + } + + /** + * @param string $constraintName + * @return ForeignKeyConstraint + */ + public function getForeignKey($constraintName) + { + $constraintName = strtolower($constraintName); + if(!$this->hasForeignKey($constraintName)) { + throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name); + } + + return $this->_fkConstraints[$constraintName]; + } + + public function removeForeignKey($constraintName) + { + $constraintName = strtolower($constraintName); + if(!$this->hasForeignKey($constraintName)) { + throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name); + } + + unset($this->_fkConstraints[$constraintName]); + } + + /** + * @return Column[] + */ + public function getColumns() + { + $columns = $this->_columns; + + $pkCols = array(); + $fkCols = array(); + + if ($this->hasPrimaryKey()) { + $pkCols = $this->getPrimaryKey()->getColumns(); + } + foreach ($this->getForeignKeys() AS $fk) { + /* @var $fk ForeignKeyConstraint */ + $fkCols = array_merge($fkCols, $fk->getColumns()); + } + $colNames = array_unique(array_merge($pkCols, $fkCols, array_keys($columns))); + + uksort($columns, function($a, $b) use($colNames) { + return (array_search($a, $colNames) >= array_search($b, $colNames)); + }); + return $columns; + } + + + /** + * Does this table have a column with the given name? + * + * @param string $columnName + * @return bool + */ + public function hasColumn($columnName) + { + $columnName = $this->trimQuotes(strtolower($columnName)); + return isset($this->_columns[$columnName]); + } + + /** + * Get a column instance + * + * @param string $columnName + * @return Column + */ + public function getColumn($columnName) + { + $columnName = strtolower($this->trimQuotes($columnName)); + if (!$this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + + return $this->_columns[$columnName]; + } + + /** + * @return Index|null + */ + public function getPrimaryKey() + { + if (!$this->hasPrimaryKey()) { + return null; + } + return $this->getIndex($this->_primaryKeyName); + } + + /** + * Check if this table has a primary key. + * + * @return bool + */ + public function hasPrimaryKey() + { + return ($this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName)); + } + + /** + * @param string $indexName + * @return bool + */ + public function hasIndex($indexName) + { + $indexName = strtolower($indexName); + return (isset($this->_indexes[$indexName])); + } + + /** + * @param string $indexName + * @return Index + */ + public function getIndex($indexName) + { + $indexName = strtolower($indexName); + if (!$this->hasIndex($indexName)) { + throw SchemaException::indexDoesNotExist($indexName, $this->_name); + } + return $this->_indexes[$indexName]; + } + + /** + * @return array + */ + public function getIndexes() + { + return $this->_indexes; + } + + /** + * Get Constraints + * + * @return array + */ + public function getForeignKeys() + { + return $this->_fkConstraints; + } + + public function hasOption($name) + { + return isset($this->_options[$name]); + } + + public function getOption($name) + { + return $this->_options[$name]; + } + + public function getOptions() + { + return $this->_options; + } + + /** + * @param Visitor $visitor + */ + public function visit(Visitor $visitor) + { + $visitor->acceptTable($this); + + foreach ($this->getColumns() AS $column) { + $visitor->acceptColumn($this, $column); + } + + foreach ($this->getIndexes() AS $index) { + $visitor->acceptIndex($this, $index); + } + + foreach ($this->getForeignKeys() AS $constraint) { + $visitor->acceptForeignKey($this, $constraint); + } + } + + /** + * Clone of a Table triggers a deep clone of all affected assets + */ + public function __clone() + { + foreach ($this->_columns AS $k => $column) { + $this->_columns[$k] = clone $column; + } + foreach ($this->_indexes AS $k => $index) { + $this->_indexes[$k] = clone $index; + } + foreach ($this->_fkConstraints AS $k => $fk) { + $this->_fkConstraints[$k] = clone $fk; + $this->_fkConstraints[$k]->setLocalTable($this); + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php new file mode 100644 index 0000000..5f99ce2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php @@ -0,0 +1,136 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Table Diff + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + * @since 2.0 + * @author Benjamin Eberlei + */ +class TableDiff +{ + /** + * @var string + */ + public $name = null; + + /** + * @var string + */ + public $newName = false; + + /** + * All added fields + * + * @var array(string=>Column) + */ + public $addedColumns; + + /** + * All changed fields + * + * @var array(string=>Column) + */ + public $changedColumns = array(); + + /** + * All removed fields + * + * @var array(string=>Column) + */ + public $removedColumns = array(); + + /** + * Columns that are only renamed from key to column instance name. + * + * @var array(string=>Column) + */ + public $renamedColumns = array(); + + /** + * All added indexes + * + * @var array(string=>Index) + */ + public $addedIndexes = array(); + + /** + * All changed indexes + * + * @var array(string=>Index) + */ + public $changedIndexes = array(); + + /** + * All removed indexes + * + * @var array(string=>bool) + */ + public $removedIndexes = array(); + + /** + * All added foreign key definitions + * + * @var array + */ + public $addedForeignKeys = array(); + + /** + * All changed foreign keys + * + * @var array + */ + public $changedForeignKeys = array(); + + /** + * All removed foreign keys + * + * @var array + */ + public $removedForeignKeys = array(); + + /** + * Constructs an TableDiff object. + * + * @param array(string=>Column) $addedColumns + * @param array(string=>Column) $changedColumns + * @param array(string=>bool) $removedColumns + * @param array(string=>Index) $addedIndexes + * @param array(string=>Index) $changedIndexes + * @param array(string=>bool) $removedIndexes + */ + public function __construct($tableName, $addedColumns = array(), + $changedColumns = array(), $removedColumns = array(), $addedIndexes = array(), + $changedIndexes = array(), $removedIndexes = array()) + { + $this->name = $tableName; + $this->addedColumns = $addedColumns; + $this->changedColumns = $changedColumns; + $this->removedColumns = $removedColumns; + $this->addedIndexes = $addedIndexes; + $this->changedIndexes = $changedIndexes; + $this->removedIndexes = $removedIndexes; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/View.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/View.php new file mode 100644 index 0000000..4a8329d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/View.php @@ -0,0 +1,53 @@ +. +*/ + +namespace Doctrine\DBAL\Schema; + +/** + * Representation of a Database View + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class View extends AbstractAsset +{ + /** + * @var string + */ + private $_sql; + + public function __construct($name, $sql) + { + $this->_setName($name); + $this->_sql = $sql; + } + + /** + * @return string + */ + public function getSql() + { + return $this->_sql; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php new file mode 100644 index 0000000..d0a5ac4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php @@ -0,0 +1,147 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\Index; + +class CreateSchemaSqlCollector implements Visitor +{ + /** + * @var array + */ + private $_createTableQueries = array(); + + /** + * @var array + */ + private $_createSequenceQueries = array(); + + /** + * @var array + */ + private $_createFkConstraintQueries = array(); + + /** + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform = null; + + /** + * @param AbstractPlatform $platform + */ + public function __construct(AbstractPlatform $platform) + { + $this->_platform = $platform; + } + + /** + * @param Schema $schema + */ + public function acceptSchema(Schema $schema) + { + + } + + /** + * Generate DDL Statements to create the accepted table with all its dependencies. + * + * @param Table $table + */ + public function acceptTable(Table $table) + { + $this->_createTableQueries = array_merge($this->_createTableQueries, + $this->_platform->getCreateTableSQL($table) + ); + } + + public function acceptColumn(Table $table, Column $column) + { + + } + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + // Append the foreign key constraints SQL + if ($this->_platform->supportsForeignKeyConstraints()) { + $this->_createFkConstraintQueries = array_merge($this->_createFkConstraintQueries, + (array) $this->_platform->getCreateForeignKeySQL( + $fkConstraint, $localTable + ) + ); + } + } + + /** + * @param Table $table + * @param Index $index + */ + public function acceptIndex(Table $table, Index $index) + { + + } + + /** + * @param Sequence $sequence + */ + public function acceptSequence(Sequence $sequence) + { + $this->_createSequenceQueries = array_merge( + $this->_createSequenceQueries, (array)$this->_platform->getCreateSequenceSQL($sequence) + ); + } + + /** + * @return array + */ + public function resetQueries() + { + $this->_createTableQueries = array(); + $this->_createSequenceQueries = array(); + $this->_createFkConstraintQueries = array(); + } + + /** + * Get all queries collected so far. + * + * @return array + */ + public function getQueries() + { + return array_merge( + $this->_createTableQueries, + $this->_createSequenceQueries, + $this->_createFkConstraintQueries + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php new file mode 100644 index 0000000..f59a7ea --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php @@ -0,0 +1,159 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\Index; + +/** + * Gather SQL statements that allow to completly drop the current schema. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DropSchemaSqlCollector implements Visitor +{ + /** + * @var \SplObjectStorage + */ + private $constraints; + + /** + * @var \SplObjectStorage + */ + private $sequences; + + /** + * @var \SplObjectStorage + */ + private $tables; + + /** + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * @param AbstractPlatform $platform + */ + public function __construct(AbstractPlatform $platform) + { + $this->platform = $platform; + $this->clearQueries(); + } + + /** + * @param Schema $schema + */ + public function acceptSchema(Schema $schema) + { + + } + + /** + * @param Table $table + */ + public function acceptTable(Table $table) + { + $this->tables->attach($table); + } + + /** + * @param Column $column + */ + public function acceptColumn(Table $table, Column $column) + { + + } + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + if (strlen($fkConstraint->getName()) == 0) { + throw SchemaException::namedForeignKeyRequired($localTable, $fkConstraint); + } + + $this->constraints->attach($fkConstraint); + $this->constraints[$fkConstraint] = $localTable; + } + + /** + * @param Table $table + * @param Index $index + */ + public function acceptIndex(Table $table, Index $index) + { + + } + + /** + * @param Sequence $sequence + */ + public function acceptSequence(Sequence $sequence) + { + $this->sequences->attach($sequence); + } + + /** + * @return void + */ + public function clearQueries() + { + $this->constraints = new \SplObjectStorage(); + $this->sequences = new \SplObjectStorage(); + $this->tables = new \SplObjectStorage(); + } + + /** + * @return array + */ + public function getQueries() + { + $sql = array(); + foreach ($this->constraints AS $fkConstraint) { + $localTable = $this->constraints[$fkConstraint]; + $sql[] = $this->platform->getDropForeignKeySQL($fkConstraint, $localTable); + } + + foreach ($this->sequences AS $sequence) { + $sql[] = $this->platform->getDropSequenceSQL($sequence); + } + + foreach ($this->tables AS $table) { + $sql[] = $this->platform->getDropTableSQL($table); + } + + return $sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php new file mode 100644 index 0000000..762f551 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php @@ -0,0 +1,151 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\Index; + +class Graphviz implements \Doctrine\DBAL\Schema\Visitor\Visitor +{ + private $output = ''; + + public function acceptColumn(Table $table, Column $column) + { + + } + + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + $this->output .= $this->createNodeRelation( + $fkConstraint->getLocalTableName() . ":col" . current($fkConstraint->getLocalColumns()).":se", + $fkConstraint->getForeignTableName() . ":col" . current($fkConstraint->getForeignColumns()).":se", + array( + 'dir' => 'back', + 'arrowtail' => 'dot', + 'arrowhead' => 'normal', + ) + ); + } + + public function acceptIndex(Table $table, Index $index) + { + + } + + public function acceptSchema(Schema $schema) + { + $this->output = 'digraph "' . sha1( mt_rand() ) . '" {' . "\n"; + $this->output .= 'splines = true;' . "\n"; + $this->output .= 'overlap = false;' . "\n"; + $this->output .= 'outputorder=edgesfirst;'."\n"; + $this->output .= 'mindist = 0.6;' . "\n"; + $this->output .= 'sep = .2;' . "\n"; + } + + public function acceptSequence(Sequence $sequence) + { + + } + + public function acceptTable(Table $table) + { + $this->output .= $this->createNode( + $table->getName(), + array( + 'label' => $this->createTableLabel( $table ), + 'shape' => 'plaintext', + ) + ); + } + + private function createTableLabel( Table $table ) + { + // Start the table + $label = '<'; + + // The title + $label .= ''; + + // The attributes block + foreach( $table->getColumns() as $column ) { + $columnLabel = $column->getName(); + + $label .= ''; + $label .= ''; + $label .= ''; + } + + // End the table + $label .= '
    ' . $table->getName() . '
    '; + $label .= '' . $columnLabel . ''; + $label .= '' . strtolower($column->getType()) . ''; + if ($table->hasPrimaryKey() && in_array($column->getName(), $table->getPrimaryKey()->getColumns())) { + $label .= "\xe2\x9c\xb7"; + } + $label .= '
    >'; + + return $label; + } + + private function createNode( $name, $options ) + { + $node = $name . " ["; + foreach( $options as $key => $value ) + { + $node .= $key . '=' . $value . ' '; + } + $node .= "]\n"; + return $node; + } + + private function createNodeRelation( $node1, $node2, $options ) + { + $relation = $node1 . ' -> ' . $node2 . ' ['; + foreach( $options as $key => $value ) + { + $relation .= $key . '=' . $value . ' '; + } + $relation .= "]\n"; + return $relation; + } + + /** + * Write dot language output to a file. This should usually be a *.dot file. + * + * You have to convert the output into a viewable format. For example use "neato" on linux systems + * and execute: + * + * neato -Tpng -o er.png er.dot + * + * @param string $filename + * @return void + */ + public function write($filename) + { + file_put_contents($filename, $this->output . "}"); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php new file mode 100644 index 0000000..f5db5af --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php @@ -0,0 +1,113 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\Index; + +/** + * Remove assets from a schema that are not in the default namespace. + * + * Some databases such as MySQL support cross databases joins, but don't + * allow to call DDLs to a database from another connected database. + * Before a schema is serialized into SQL this visitor can cleanup schemas with + * non default namespaces. + * + * This visitor filters all these non-default namespaced tables and sequences + * and removes them from the SChema instance. + * + * @author Benjamin Eberlei + * @since 2.2 + */ +class RemoveNamespacedAssets implements Visitor +{ + /** + * @var Schema + */ + private $schema; + + /** + * @param Schema $schema + */ + public function acceptSchema(Schema $schema) + { + $this->schema = $schema; + } + + /** + * @param Table $table + */ + public function acceptTable(Table $table) + { + if ( ! $table->isInDefaultNamespace($this->schema->getName()) ) { + $this->schema->dropTable($table->getName()); + } + } + /** + * @param Sequence $sequence + */ + public function acceptSequence(Sequence $sequence) + { + if ( ! $sequence->isInDefaultNamespace($this->schema->getName()) ) { + $this->schema->dropSequence($sequence->getName()); + } + } + + /** + * @param Column $column + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + // The table may already be deleted in a previous + // RemoveNamespacedAssets#acceptTable call. Removing Foreign keys that + // point to nowhere. + if ( ! $this->schema->hasTable($fkConstraint->getForeignTableName())) { + $localTable->removeForeignKey($fkConstraint->getName()); + return; + } + + $foreignTable = $this->schema->getTable($fkConstraint->getForeignTableName()); + if ( ! $foreignTable->isInDefaultNamespace($this->schema->getName()) ) { + $localTable->removeForeignKey($fkConstraint->getName()); + } + } + + /** + * @param Table $table + * @param Index $index + */ + public function acceptIndex(Table $table, Index $index) + { + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php new file mode 100644 index 0000000..2dad80d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\Schema\Table, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Column, + Doctrine\DBAL\Schema\ForeignKeyConstraint, + Doctrine\DBAL\Schema\Constraint, + Doctrine\DBAL\Schema\Sequence, + Doctrine\DBAL\Schema\Index; + +/** + * Schema Visitor used for Validation or Generation purposes. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +interface Visitor +{ + /** + * @param Schema $schema + */ + public function acceptSchema(Schema $schema); + + /** + * @param Table $table + */ + public function acceptTable(Table $table); + + /** + * @param Column $column + */ + public function acceptColumn(Table $table, Column $column); + + /** + * @param Table $localTable + * @param ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint); + + /** + * @param Table $table + * @param Index $index + */ + public function acceptIndex(Table $table, Index $index); + + /** + * @param Sequence $sequence + */ + public function acceptSequence(Sequence $sequence); +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php new file mode 100644 index 0000000..cf625ad --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php @@ -0,0 +1,247 @@ +. + */ + +namespace Doctrine\DBAL; + +use PDO, + Doctrine\DBAL\Types\Type, + Doctrine\DBAL\Driver\Statement as DriverStatement; + +/** + * A thin wrapper around a Doctrine\DBAL\Driver\Statement that adds support + * for logging, DBAL mapping types, etc. + * + * @author Roman Borschel + * @since 2.0 + */ +class Statement implements \IteratorAggregate, DriverStatement +{ + /** + * @var string The SQL statement. + */ + protected $sql; + /** + * @var array The bound parameters. + */ + protected $params = array(); + /** + * @var Doctrine\DBAL\Driver\Statement The underlying driver statement. + */ + protected $stmt; + /** + * @var Doctrine\DBAL\Platforms\AbstractPlatform The underlying database platform. + */ + protected $platform; + /** + * @var Doctrine\DBAL\Connection The connection this statement is bound to and executed on. + */ + protected $conn; + + /** + * Creates a new Statement for the given SQL and Connection. + * + * @param string $sql The SQL of the statement. + * @param Doctrine\DBAL\Connection The connection on which the statement should be executed. + */ + public function __construct($sql, Connection $conn) + { + $this->sql = $sql; + $this->stmt = $conn->getWrappedConnection()->prepare($sql); + $this->conn = $conn; + $this->platform = $conn->getDatabasePlatform(); + } + + /** + * Binds a parameter value to the statement. + * + * The value can optionally be bound with a PDO binding type or a DBAL mapping type. + * If bound with a DBAL mapping type, the binding type is derived from the mapping + * type and the value undergoes the conversion routines of the mapping type before + * being bound. + * + * @param $name The name or position of the parameter. + * @param $value The value of the parameter. + * @param mixed $type Either a PDO binding type or a DBAL mapping type name or instance. + * @return boolean TRUE on success, FALSE on failure. + */ + public function bindValue($name, $value, $type = null) + { + $this->params[$name] = $value; + if ($type !== null) { + if (is_string($type)) { + $type = Type::getType($type); + } + if ($type instanceof Type) { + $value = $type->convertToDatabaseValue($value, $this->platform); + $bindingType = $type->getBindingType(); + } else { + $bindingType = $type; // PDO::PARAM_* constants + } + return $this->stmt->bindValue($name, $value, $bindingType); + } else { + return $this->stmt->bindValue($name, $value); + } + } + + /** + * Binds a parameter to a value by reference. + * + * Binding a parameter by reference does not support DBAL mapping types. + * + * @param string $name The name or position of the parameter. + * @param mixed $value The reference to the variable to bind + * @param integer $type The PDO binding type. + * @return boolean TRUE on success, FALSE on failure. + */ + public function bindParam($name, &$var, $type = PDO::PARAM_STR) + { + return $this->stmt->bindParam($name, $var, $type); + } + + /** + * Executes the statement with the currently bound parameters. + * + * @return boolean TRUE on success, FALSE on failure. + */ + public function execute($params = null) + { + $hasLogger = $this->conn->getConfiguration()->getSQLLogger(); + if ($hasLogger) { + $this->conn->getConfiguration()->getSQLLogger()->startQuery($this->sql, $this->params); + } + + $stmt = $this->stmt->execute($params); + + if ($hasLogger) { + $this->conn->getConfiguration()->getSQLLogger()->stopQuery(); + } + $this->params = array(); + return $stmt; + } + + /** + * Closes the cursor, freeing the database resources used by this statement. + * + * @return boolean TRUE on success, FALSE on failure. + */ + public function closeCursor() + { + return $this->stmt->closeCursor(); + } + + /** + * Returns the number of columns in the result set. + * + * @return integer + */ + public function columnCount() + { + return $this->stmt->columnCount(); + } + + /** + * Fetches the SQLSTATE associated with the last operation on the statement. + * + * @return string + */ + public function errorCode() + { + return $this->stmt->errorCode(); + } + + /** + * Fetches extended error information associated with the last operation on the statement. + * + * @return array + */ + public function errorInfo() + { + return $this->stmt->errorInfo(); + } + + public function setFetchMode($fetchStyle, $arg2 = null, $arg3 = null) + { + return $this->stmt->setFetchMode($fetchStyle, $arg2, $arg3); + } + + public function getIterator() + { + return $this->stmt; + } + + /** + * Fetches the next row from a result set. + * + * @param integer $fetchStyle + * @return mixed The return value of this function on success depends on the fetch type. + * In all cases, FALSE is returned on failure. + */ + public function fetch($fetchStyle = PDO::FETCH_BOTH) + { + return $this->stmt->fetch($fetchStyle); + } + + /** + * Returns an array containing all of the result set rows. + * + * @param integer $fetchStyle + * @param mixed $fetchArgument + * @return array An array containing all of the remaining rows in the result set. + */ + public function fetchAll($fetchStyle = PDO::FETCH_BOTH, $fetchArgument = 0) + { + if ($fetchArgument !== 0) { + return $this->stmt->fetchAll($fetchStyle, $fetchArgument); + } + return $this->stmt->fetchAll($fetchStyle); + } + + /** + * Returns a single column from the next row of a result set. + * + * @param integer $columnIndex + * @return mixed A single column from the next row of a result set or FALSE if there are no more rows. + */ + public function fetchColumn($columnIndex = 0) + { + return $this->stmt->fetchColumn($columnIndex); + } + + /** + * Returns the number of rows affected by the last execution of this statement. + * + * @return integer The number of affected rows. + */ + public function rowCount() + { + return $this->stmt->rowCount(); + } + + /** + * Gets the wrapped driver statement. + * + * @return Doctrine\DBAL\Driver\Statement + */ + public function getWrappedStatement() + { + return $this->stmt; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php new file mode 100644 index 0000000..96eaa11 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console; + +/** + * Task for executing arbitrary SQL that can come from a file or directly from + * the command line. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ImportCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('dbal:import') + ->setDescription('Import SQL file(s) directly to Database.') + ->setDefinition(array( + new InputArgument( + 'file', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'File path(s) of SQL to be executed.' + ) + )) + ->setHelp(<<getHelper('db')->getConnection(); + + if (($fileNames = $input->getArgument('file')) !== null) { + foreach ((array) $fileNames as $fileName) { + $fileName = realpath($fileName); + + if ( ! file_exists($fileName)) { + throw new \InvalidArgumentException( + sprintf("SQL file '%s' does not exist.", $fileName) + ); + } else if ( ! is_readable($fileName)) { + throw new \InvalidArgumentException( + sprintf("SQL file '%s' does not have read permissions.", $fileName) + ); + } + + $output->write(sprintf("Processing file '%s'... ", $fileName)); + $sql = file_get_contents($fileName); + + if ($conn instanceof \Doctrine\DBAL\Driver\PDOConnection) { + // PDO Drivers + try { + $lines = 0; + + $stmt = $conn->prepare($sql); + $stmt->execute(); + + do { + // Required due to "MySQL has gone away!" issue + $stmt->fetch(); + $stmt->closeCursor(); + + $lines++; + } while ($stmt->nextRowset()); + + $output->write(sprintf('%d statements executed!', $lines) . PHP_EOL); + } catch (\PDOException $e) { + $output->write('error!' . PHP_EOL); + + throw new \RuntimeException($e->getMessage(), $e->getCode(), $e); + } + } else { + // Non-PDO Drivers (ie. OCI8 driver) + $stmt = $conn->prepare($sql); + $rs = $stmt->execute(); + + if ($rs) { + $output->writeln('OK!' . PHP_EOL); + } else { + $error = $stmt->errorInfo(); + + $output->write('error!' . PHP_EOL); + + throw new \RuntimeException($error[2], $error[0]); + } + + $stmt->closeCursor(); + } + } + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php new file mode 100644 index 0000000..9bf0a27 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php @@ -0,0 +1,133 @@ +. + */ + + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console\Command\Command, + Symfony\Component\Console\Input\InputInterface, + Symfony\Component\Console\Output\OutputInterface; +use Doctrine\DBAL\Platforms\Keywords\ReservedKeywordsValidator; + +class ReservedWordsCommand extends Command +{ + private $keywordListClasses = array( + 'mysql' => 'Doctrine\DBAL\Platforms\Keywords\MySQLKeywords', + 'mssql' => 'Doctrine\DBAL\Platforms\Keywords\MsSQLKeywords', + 'sqlite' => 'Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords', + 'pgsql' => 'Doctrine\DBAL\Platforms\Keywords\PostgreSQLKeywords', + 'oracle' => 'Doctrine\DBAL\Platforms\Keywords\OracleKeywords', + 'db2' => 'Doctrine\DBAL\Platforms\Keywords\DB2Keywords', + ); + + /** + * If you want to add or replace a keywords list use this command + * + * @param string $name + * @param string $class + */ + public function setKeywordListClass($name, $class) + { + $this->keywordListClasses[$name] = $class; + } + + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('dbal:reserved-words') + ->setDescription('Checks if the current database contains identifiers that are reserved.') + ->setDefinition(array( + new InputOption( + 'list', 'l', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Keyword-List name.' + ) + )) + ->setHelp(<<doctrine dbal:reserved-words + +If you want to check against specific dialects you can +pass them to the command: + + doctrine dbal:reserved-words mysql pgsql + +The following keyword lists are currently shipped with Doctrine: + + * mysql + * pgsql + * sqlite + * oracle + * mssql + * db2 (Not checked by default) +EOT + ); + } + + /** + * @see Console\Command\Command + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + /* @var $conn Doctrine\DBAL\Connection */ + $conn = $this->getHelper('db')->getConnection(); + + $keywordLists = (array)$input->getOption('list'); + if (!$keywordLists) { + $keywordLists = array('mysql', 'pgsql', 'sqlite', 'oracle', 'mssql'); + } + + $keywords = array(); + foreach ($keywordLists AS $keywordList) { + if (!isset($this->keywordListClasses[$keywordList])) { + throw new \InvalidArgumentException( + "There exists no keyword list with name '" . $keywordList . "'. ". + "Known lists: " . implode(", ", array_keys($this->keywordListClasses)) + ); + } + $class = $this->keywordListClasses[$keywordList]; + $keywords[] = new $class; + } + + $output->write('Checking keyword violations for ' . implode(", ", $keywordLists) . "...", true); + + /* @var $schema \Doctrine\DBAL\Schema\Schema */ + $schema = $conn->getSchemaManager()->createSchema(); + $visitor = new ReservedKeywordsValidator($keywords); + $schema->visit($visitor); + + $violations = $visitor->getViolations(); + if (count($violations) == 0) { + $output->write("No reserved keywords violations have been found!", true); + } else { + $output->write('There are ' . count($violations) . ' reserved keyword violations in your database schema:', true); + foreach ($violations AS $violation) { + $output->write(' - ' . $violation, true); + } + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php new file mode 100644 index 0000000..856c1a2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php @@ -0,0 +1,87 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console; + +/** + * Task for executing arbitrary SQL that can come from a file or directly from + * the command line. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class RunSqlCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('dbal:run-sql') + ->setDescription('Executes arbitrary SQL directly from the command line.') + ->setDefinition(array( + new InputArgument('sql', InputArgument::REQUIRED, 'The SQL statement to execute.'), + new InputOption('depth', null, InputOption::VALUE_REQUIRED, 'Dumping depth of result set.', 7) + )) + ->setHelp(<<getHelper('db')->getConnection(); + + if (($sql = $input->getArgument('sql')) === null) { + throw new \RuntimeException("Argument 'SQL' is required in order to execute this command correctly."); + } + + $depth = $input->getOption('depth'); + + if ( ! is_numeric($depth)) { + throw new \LogicException("Option 'depth' must contains an integer value"); + } + + if (preg_match('/^select/i', $sql)) { + $resultSet = $conn->fetchAll($sql); + } else { + $resultSet = $conn->executeUpdate($sql); + } + + ob_start(); + \Doctrine\Common\Util\Debug::dump($resultSet, (int) $depth); + $message = ob_get_clean(); + + $output->write($message); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Helper/ConnectionHelper.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Helper/ConnectionHelper.php new file mode 100644 index 0000000..4437d46 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Helper/ConnectionHelper.php @@ -0,0 +1,74 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Helper; + +use Symfony\Component\Console\Helper\Helper, + Doctrine\DBAL\Connection; + +/** + * Doctrine CLI Connection Helper. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConnectionHelper extends Helper +{ + /** + * Doctrine Database Connection + * @var Connection + */ + protected $_connection; + + /** + * Constructor + * + * @param Connection $connection Doctrine Database Connection + */ + public function __construct(Connection $connection) + { + $this->_connection = $connection; + } + + /** + * Retrieves Doctrine Database Connection + * + * @return Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @see Helper + */ + public function getName() + { + return 'connection'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php new file mode 100644 index 0000000..237070e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +/** + * Type that maps a PHP array to a clob SQL type. + * + * @since 2.0 + */ +class ArrayType extends Type +{ + public function getSQLDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + return serialize($value); + } + + public function convertToPHPValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + $val = unserialize($value); + if ($val === false && $value != 'b:0;') { + throw ConversionException::conversionFailed($value, $this->getName()); + } + return $val; + } + + public function getName() + { + return Type::TARRAY; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php new file mode 100644 index 0000000..6c3f4b0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a database BIGINT to a PHP string. + * + * @author robo + * @since 2.0 + */ +class BigIntType extends Type +{ + public function getName() + { + return Type::BIGINT; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBigIntTypeDeclarationSQL($fieldDeclaration); + } + + public function getBindingType() + { + return \PDO::PARAM_STR; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php new file mode 100644 index 0000000..5be8c30 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL BLOB to a PHP resource stream + * + * @since 2.2 + */ +class BlobType extends Type +{ + /** @override */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBlobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + if (is_string($value)) { + $value = fopen('data://text/plain;base64,' . base64_encode($value), 'r'); + } else if ( ! is_resource($value)) { + throw ConversionException::conversionFailed($value, self::BLOB); + } + return $value; + } + + public function getName() + { + return Type::BLOB; + } + + public function getBindingType() + { + return \PDO::PARAM_LOB; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php new file mode 100644 index 0000000..e3cb9a1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL boolean to a PHP boolean. + * + * @since 2.0 + */ +class BooleanType extends Type +{ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBooleanTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return $platform->convertBooleans($value); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (bool) $value; + } + + public function getName() + { + return Type::BOOLEAN; + } + + public function getBindingType() + { + return \PDO::PARAM_BOOL; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php new file mode 100644 index 0000000..ef3270f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php @@ -0,0 +1,65 @@ +. + */ + + +/** + * Conversion Exception is thrown when the database to PHP conversion fails + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +namespace Doctrine\DBAL\Types; + +class ConversionException extends \Doctrine\DBAL\DBALException +{ + /** + * Thrown when a Database to Doctrine Type Conversion fails. + * + * @param string $value + * @param string $toType + * @return ConversionException + */ + static public function conversionFailed($value, $toType) + { + $value = (strlen($value) > 32) ? substr($value, 0, 20) . "..." : $value; + return new self('Could not convert database value "' . $value . '" to Doctrine Type ' . $toType); + } + + /** + * Thrown when a Database to Doctrine Type Conversion fails and we can make a statement + * about the expected format. + * + * @param string $value + * @param string $toType + * @return ConversionException + */ + static public function conversionFailedFormat($value, $toType, $expectedFormat) + { + $value = (strlen($value) > 32) ? substr($value, 0, 20) . "..." : $value; + return new self( + 'Could not convert database value "' . $value . '" to Doctrine Type ' . + $toType . '. Expected format: ' . $expectedFormat + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php new file mode 100644 index 0000000..d073173 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL DATETIME/TIMESTAMP to a PHP DateTime object. + * + * @since 2.0 + */ +class DateTimeType extends Type +{ + public function getName() + { + return Type::DATETIME; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTimeTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateTimeFormatString()) : null; + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $val = \DateTime::createFromFormat($platform->getDateTimeFormatString(), $value); + if (!$val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateTimeFormatString()); + } + return $val; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php new file mode 100644 index 0000000..4dc6058 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php @@ -0,0 +1,79 @@ +. + */ + + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * DateTime type saving additional timezone information. + * + * Caution: Databases are not necessarily experts at storing timezone related + * data of dates. First, of all the supported vendors only PostgreSQL and Oracle + * support storing Timezone data. But those two don't save the actual timezone + * attached to a DateTime instance (for example "Europe/Berlin" or "America/Montreal") + * but the current offset of them related to UTC. That means depending on daylight saving times + * or not you may get different offsets. + * + * This datatype makes only sense to use, if your application works with an offset, not + * with an actual timezone that uses transitions. Otherwise your DateTime instance + * attached with a timezone such as Europe/Berlin gets saved into the database with + * the offset and re-created from persistence with only the offset, not the original timezone + * attached. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DateTimeTzType extends Type +{ + public function getName() + { + return Type::DATETIMETZ; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTimeTzTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateTimeTzFormatString()) : null; + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $val = \DateTime::createFromFormat($platform->getDateTimeTzFormatString(), $value); + if (!$val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateTimeTzFormatString()); + } + return $val; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php new file mode 100644 index 0000000..42c4803 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL DATE to a PHP Date object. + * + * @since 2.0 + */ +class DateType extends Type +{ + public function getName() + { + return Type::DATE; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateFormatString()) : null; + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $val = \DateTime::createFromFormat('!'.$platform->getDateFormatString(), $value); + if (!$val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateFormatString()); + } + return $val; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php new file mode 100644 index 0000000..1308c96 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL DECIMAL to a PHP double. + * + * @since 2.0 + */ +class DecimalType extends Type +{ + public function getName() + { + return Type::DECIMAL; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDecimalTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : $value; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php new file mode 100644 index 0000000..c8f7914 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php @@ -0,0 +1,54 @@ +. + */ + + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +class FloatType extends Type +{ + public function getName() + { + return Type::FLOAT; + } + + /** + * @param array $fieldDeclaration + * @param AbstractPlatform $platform + * @return string + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getFloatDeclarationSQL($fieldDeclaration); + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (double) $value; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php new file mode 100644 index 0000000..dd13f0a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL INT to a PHP integer. + * + * @author Roman Borschel + * @since 2.0 + */ +class IntegerType extends Type +{ + public function getName() + { + return Type::INTEGER; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getIntegerTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (int) $value; + } + + public function getBindingType() + { + return \PDO::PARAM_INT; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php new file mode 100644 index 0000000..977c3b3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +/** + * Type that maps a PHP object to a clob SQL type. + * + * @since 2.0 + */ +class ObjectType extends Type +{ + public function getSQLDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + return serialize($value); + } + + public function convertToPHPValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + $val = unserialize($value); + if ($val === false && $value !== 'b:0;') { + throw ConversionException::conversionFailed($value, $this->getName()); + } + return $val; + } + + public function getName() + { + return Type::OBJECT; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php new file mode 100644 index 0000000..5e1df69 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a database SMALLINT to a PHP integer. + * + * @author robo + */ +class SmallIntType extends Type +{ + public function getName() + { + return Type::SMALLINT; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getSmallIntTypeDeclarationSQL($fieldDeclaration); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (int) $value; + } + + public function getBindingType() + { + return \PDO::PARAM_INT; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/StringType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/StringType.php new file mode 100644 index 0000000..a813eaa --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/StringType.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL VARCHAR to a PHP string. + * + * @since 2.0 + */ +class StringType extends Type +{ + /** @override */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + } + + /** @override */ + public function getDefaultLength(AbstractPlatform $platform) + { + return $platform->getVarcharDefaultLength(); + } + + /** @override */ + public function getName() + { + return Type::STRING; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php new file mode 100644 index 0000000..50b4f83 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL CLOB to a PHP string. + * + * @since 2.0 + */ +class TextType extends Type +{ + /** @override */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (is_resource($value)) ? stream_get_contents($value) : $value; + } + + public function getName() + { + return Type::TEXT; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php new file mode 100644 index 0000000..d4c49c4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL TIME to a PHP DateTime object. + * + * @since 2.0 + */ +class TimeType extends Type +{ + public function getName() + { + return Type::TIME; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getTimeTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getTimeFormatString()) : null; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $val = \DateTime::createFromFormat($platform->getTimeFormatString(), $value); + if (!$val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getTimeFormatString()); + } + return $val; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php new file mode 100644 index 0000000..320dfa9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php @@ -0,0 +1,274 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform, + Doctrine\DBAL\DBALException; + +/** + * The base class for so-called Doctrine mapping types. + * + * A Type object is obtained by calling the static {@link getType()} method. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +abstract class Type +{ + const TARRAY = 'array'; + const BIGINT = 'bigint'; + const BOOLEAN = 'boolean'; + const DATETIME = 'datetime'; + const DATETIMETZ = 'datetimetz'; + const DATE = 'date'; + const TIME = 'time'; + const DECIMAL = 'decimal'; + const INTEGER = 'integer'; + const OBJECT = 'object'; + const SMALLINT = 'smallint'; + const STRING = 'string'; + const TEXT = 'text'; + const BLOB = 'blob'; + const FLOAT = 'float'; + + /** Map of already instantiated type objects. One instance per type (flyweight). */ + private static $_typeObjects = array(); + + /** The map of supported doctrine mapping types. */ + private static $_typesMap = array( + self::TARRAY => 'Doctrine\DBAL\Types\ArrayType', + self::OBJECT => 'Doctrine\DBAL\Types\ObjectType', + self::BOOLEAN => 'Doctrine\DBAL\Types\BooleanType', + self::INTEGER => 'Doctrine\DBAL\Types\IntegerType', + self::SMALLINT => 'Doctrine\DBAL\Types\SmallIntType', + self::BIGINT => 'Doctrine\DBAL\Types\BigIntType', + self::STRING => 'Doctrine\DBAL\Types\StringType', + self::TEXT => 'Doctrine\DBAL\Types\TextType', + self::DATETIME => 'Doctrine\DBAL\Types\DateTimeType', + self::DATETIMETZ => 'Doctrine\DBAL\Types\DateTimeTzType', + self::DATE => 'Doctrine\DBAL\Types\DateType', + self::TIME => 'Doctrine\DBAL\Types\TimeType', + self::DECIMAL => 'Doctrine\DBAL\Types\DecimalType', + self::FLOAT => 'Doctrine\DBAL\Types\FloatType', + self::BLOB => 'Doctrine\DBAL\Types\BlobType', + ); + + /* Prevent instantiation and force use of the factory method. */ + final private function __construct() {} + + /** + * Converts a value from its PHP representation to its database representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The database representation of the value. + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return $value; + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param AbstractPlatform $platform The currently used database platform. + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return $value; + } + + /** + * Gets the default length of this type. + * + * @todo Needed? + */ + public function getDefaultLength(AbstractPlatform $platform) + { + return null; + } + + /** + * Gets the SQL declaration snippet for a field of this type. + * + * @param array $fieldDeclaration The field declaration. + * @param AbstractPlatform $platform The currently used database platform. + */ + abstract public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform); + + /** + * Gets the name of this type. + * + * @return string + * @todo Needed? + */ + abstract public function getName(); + + /** + * Factory method to create type instances. + * Type instances are implemented as flyweights. + * + * @static + * @throws DBALException + * @param string $name The name of the type (as returned by getName()). + * @return Doctrine\DBAL\Types\Type + */ + public static function getType($name) + { + if ( ! isset(self::$_typeObjects[$name])) { + if ( ! isset(self::$_typesMap[$name])) { + throw DBALException::unknownColumnType($name); + } + self::$_typeObjects[$name] = new self::$_typesMap[$name](); + } + + return self::$_typeObjects[$name]; + } + + /** + * Adds a custom type to the type map. + * + * @static + * @param string $name Name of the type. This should correspond to what getName() returns. + * @param string $className The class name of the custom type. + * @throws DBALException + */ + public static function addType($name, $className) + { + if (isset(self::$_typesMap[$name])) { + throw DBALException::typeExists($name); + } + + self::$_typesMap[$name] = $className; + } + + /** + * Checks if exists support for a type. + * + * @static + * @param string $name Name of the type + * @return boolean TRUE if type is supported; FALSE otherwise + */ + public static function hasType($name) + { + return isset(self::$_typesMap[$name]); + } + + /** + * Overrides an already defined type to use a different implementation. + * + * @static + * @param string $name + * @param string $className + * @throws DBALException + */ + public static function overrideType($name, $className) + { + if ( ! isset(self::$_typesMap[$name])) { + throw DBALException::typeNotFound($name); + } + + if (isset(self::$_typeObjects[$name])) { + unset(self::$_typeObjects[$name]); + } + + self::$_typesMap[$name] = $className; + } + + /** + * Gets the (preferred) binding type for values of this type that + * can be used when binding parameters to prepared statements. + * + * This method should return one of the PDO::PARAM_* constants, that is, one of: + * + * PDO::PARAM_BOOL + * PDO::PARAM_NULL + * PDO::PARAM_INT + * PDO::PARAM_STR + * PDO::PARAM_LOB + * + * @return integer + */ + public function getBindingType() + { + return \PDO::PARAM_STR; + } + + /** + * Get the types array map which holds all registered types and the corresponding + * type class + * + * @return array $typesMap + */ + public static function getTypesMap() + { + return self::$_typesMap; + } + + public function __toString() + { + $e = explode('\\', get_class($this)); + return str_replace('Type', '', end($e)); + } + + /** + * Does working with this column require SQL conversion functions? + * + * This is a metadata function that is required for example in the ORM. + * Usage of {@link convertToDatabaseValueSQL} and + * {@link convertToPHPValueSQL} works for any type and mostly + * does nothing. This method can additionally be used for optimization purposes. + * + * @return bool + */ + public function canRequireSQLConversion() + { + return false; + } + + /** + * Modifies the SQL expression (identifier, parameter) to convert to a database value. + * + * @param string $sqlExpr + * @param AbstractPlatform $platform + * @return string + */ + public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) + { + return $sqlExpr; + } + + /** + * Modifies the SQL expression (identifier, parameter) to convert to a PHP value. + * + * @param string $sqlExpr + * @param AbstractPlatform $platform + * @return string + */ + public function convertToPHPValueSQL($sqlExpr, $platform) + { + return $sqlExpr; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php new file mode 100644 index 0000000..03ea048 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php @@ -0,0 +1,60 @@ +. + */ + + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Variable DateTime Type using date_create() instead of DateTime::createFromFormat() + * + * This type has performance implications as it runs twice as long as the regular + * {@see DateTimeType}, however in certain PostgreSQL configurations with + * TIMESTAMP(n) columns where n > 0 it is necessary to use this type. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class VarDateTimeType extends DateTimeType +{ + /** + * @throws ConversionException + * @param string $value + * @param AbstractPlatform $platform + * @return DateTime + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $val = date_create($value); + if (!$val) { + throw ConversionException::conversionFailed($value, $this->getName()); + } + return $val; + } +} \ No newline at end of file diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php new file mode 100644 index 0000000..883b217 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Class to store and retrieve the version of Doctrine + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Version +{ + /** + * Current Doctrine Version + */ + const VERSION = '2.2.3-DEV'; + + /** + * Compares a Doctrine version with the current one. + * + * @param string $version Doctrine version to compare. + * @return int Returns -1 if older, 0 if it is the same, 1 if version + * passed as argument is newer. + */ + public static function compare($version) + { + $currentVersion = str_replace(' ', '', strtolower(self::VERSION)); + $version = str_replace(' ', '', $version); + + return version_compare($version, $currentVersion); + } +} diff --git a/vendor/doctrine/dbal/phpunit.xml.dist b/vendor/doctrine/dbal/phpunit.xml.dist new file mode 100644 index 0000000..fe1d515 --- /dev/null +++ b/vendor/doctrine/dbal/phpunit.xml.dist @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + ./tests/Doctrine/Tests/DBAL + + + \ No newline at end of file diff --git a/vendor/doctrine/dbal/run-all.sh b/vendor/doctrine/dbal/run-all.sh new file mode 100755 index 0000000..80712ee --- /dev/null +++ b/vendor/doctrine/dbal/run-all.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# This script is a small convenience wrapper for running the doctrine testsuite against a large bunch of databases. +# Just create the phpunit.xmls as described in the array below and configure the specific files section +# to connect to that database. Just omit a file if you dont have that database and the tests will be skipped. + +configs[1]="mysql.phpunit.xml" +configs[2]='postgres.phpunit.xml' +configs[3]='sqlite.phpunit.xml' +configs[4]='oracle.phpunit.xml' +configs[5]='db2.phpunit.xml' +configs[6]='pdo-ibm.phpunit.xml' +configs[7]='sqlsrv.phpunit.xml' + +for i in "${configs[@]}"; do + if [ -f "$i" ]; + then + echo "RUNNING TESTS WITH CONFIG $i" + phpunit -c "$i" "$@" + fi; +done diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/.travis.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/.travis.yml new file mode 100644 index 0000000..47ab323 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/.travis.yml @@ -0,0 +1,10 @@ +language: php + +php: + - 5.3 + - 5.4 + +before_script: + - wget http://getcomposer.org/composer.phar + - php composer.phar install --dev + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/CreateDatabaseDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/CreateDatabaseDoctrineCommand.php new file mode 100644 index 0000000..c9bb508 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/CreateDatabaseDoctrineCommand.php @@ -0,0 +1,76 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\DBAL\DriverManager; + +/** + * Database tool allows you to easily drop and create your configured databases. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class CreateDatabaseDoctrineCommand extends DoctrineCommand +{ + protected function configure() + { + $this + ->setName('doctrine:database:create') + ->setDescription('Creates the configured databases') + ->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'The connection to use for this command') + ->setHelp(<<doctrine:database:create command creates the default +connections database: + +php app/console doctrine:database:create + +You can also optionally specify the name of a connection to create the +database for: + +php app/console doctrine:database:create --connection=default +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $connection = $this->getDoctrineConnection($input->getOption('connection')); + + $params = $connection->getParams(); + $name = isset($params['path']) ? $params['path'] : $params['dbname']; + + unset($params['dbname']); + + $tmpConnection = DriverManager::getConnection($params); + + // Only quote if we don't have a path + if (!isset($params['path'])) { + $name = $tmpConnection->getDatabasePlatform()->quoteSingleIdentifier($name); + } + + try { + $tmpConnection->getSchemaManager()->createDatabase($name); + $output->writeln(sprintf('Created database for connection named %s', $name)); + } catch (\Exception $e) { + $output->writeln(sprintf('Could not create database for connection named %s', $name)); + $output->writeln(sprintf('%s', $e->getMessage())); + } + + $tmpConnection->close(); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/DoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/DoctrineCommand.php new file mode 100644 index 0000000..f5d3f0e --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/DoctrineCommand.php @@ -0,0 +1,55 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Doctrine\ORM\Tools\EntityGenerator; + +/** + * Base class for Doctrine console commands to extend from. + * + * @author Fabien Potencier + */ +abstract class DoctrineCommand extends ContainerAwareCommand +{ + protected function getEntityGenerator() + { + $entityGenerator = new EntityGenerator(); + $entityGenerator->setGenerateAnnotations(false); + $entityGenerator->setGenerateStubMethods(true); + $entityGenerator->setRegenerateEntityIfExists(false); + $entityGenerator->setUpdateEntityIfExists(true); + $entityGenerator->setNumSpaces(4); + $entityGenerator->setAnnotationPrefix('ORM\\'); + + return $entityGenerator; + } + + protected function getEntityManager($name) + { + return $this->getContainer()->get('doctrine')->getManager($name); + } + + /** + * Get a doctrine dbal connection by symfony name. + * + * @param string $name + * @return Doctrine\DBAL\Connection + */ + protected function getDoctrineConnection($name) + { + return $this->getContainer()->get('doctrine')->getConnection($name); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/DropDatabaseDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/DropDatabaseDoctrineCommand.php new file mode 100644 index 0000000..d0f0e39 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/DropDatabaseDoctrineCommand.php @@ -0,0 +1,88 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Database tool allows you to easily drop and create your configured databases. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class DropDatabaseDoctrineCommand extends DoctrineCommand +{ + protected function configure() + { + $this + ->setName('doctrine:database:drop') + ->setDescription('Drops the configured databases') + ->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'The connection to use for this command') + ->addOption('force', null, InputOption::VALUE_NONE, 'Set this parameter to execute this action') + ->setHelp(<<doctrine:database:drop command drops the default connections +database: + +php app/console doctrine:database:drop + +The --force parameter has to be used to actually drop the database. + +You can also optionally specify the name of a connection to drop the database +for: + +php app/console doctrine:database:drop --connection=default + +Be careful: All data in a given database will be lost when executing +this command. +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $connection = $this->getDoctrineConnection($input->getOption('connection')); + + $params = $connection->getParams(); + + $name = isset($params['path']) ? $params['path'] : (isset($params['dbname']) ? $params['dbname'] : false); + + if (!$name) { + throw new \InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped."); + } + + if ($input->getOption('force')) { + // Only quote if we don't have a path + if (!isset($params['path'])) { + $name = $connection->getDatabasePlatform()->quoteSingleIdentifier($name); + } + + try { + $connection->getSchemaManager()->dropDatabase($name); + $output->writeln(sprintf('Dropped database for connection named %s', $name)); + } catch (\Exception $e) { + $output->writeln(sprintf('Could not drop database for connection named %s', $name)); + $output->writeln(sprintf('%s', $e->getMessage())); + } + } else { + $output->writeln('ATTENTION: This operation should not be executed in a production environment.'); + $output->writeln(''); + $output->writeln(sprintf('Would drop the database named %s.', $name)); + $output->writeln('Please run the operation with --force to execute'); + $output->writeln('All data will be lost!'); + } + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/GenerateEntitiesDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/GenerateEntitiesDoctrineCommand.php new file mode 100644 index 0000000..4cbc64b --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/GenerateEntitiesDoctrineCommand.php @@ -0,0 +1,134 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\EntityRepositoryGenerator; +use Doctrine\Bundle\DoctrineBundle\Mapping\DisconnectedMetadataFactory; + +/** + * Generate entity classes from mapping information + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class GenerateEntitiesDoctrineCommand extends DoctrineCommand +{ + protected function configure() + { + $this + ->setName('doctrine:generate:entities') + ->setAliases(array('generate:doctrine:entities')) + ->setDescription('Generates entity classes and method stubs from your mapping information') + ->addArgument('name', InputArgument::REQUIRED, 'A bundle name, a namespace, or a class name') + ->addOption('path', null, InputOption::VALUE_REQUIRED, 'The path where to generate entities when it cannot be guessed') + ->addOption('no-backup', null, InputOption::VALUE_NONE, 'Do not backup existing entities files.') + ->setHelp(<<doctrine:generate:entities command generates entity classes +and method stubs from your mapping information: + +You have to limit generation of entities: + +* To a bundle: + + php app/console doctrine:generate:entities MyCustomBundle + +* To a single entity: + + php app/console doctrine:generate:entities MyCustomBundle:User + php app/console doctrine:generate:entities MyCustomBundle/Entity/User + +* To a namespace + + php app/console doctrine:generate:entities MyCustomBundle/Entity + +If the entities are not stored in a bundle, and if the classes do not exist, +the command has no way to guess where they should be generated. In this case, +you must provide the --path option: + + php app/console doctrine:generate:entities Blog/Entity --path=src/ + +By default, the unmodified version of each entity is backed up and saved +(e.g. Product.php~). To prevent this task from creating the backup file, +pass the --no-backup option: + + php app/console doctrine:generate:entities Blog/Entity --no-backup + +Important: Even if you specified Inheritance options in your +XML or YAML Mapping files the generator cannot generate the base and +child classes for you correctly, because it doesn't know which +class is supposed to extend which. You have to adjust the entity +code manually for inheritance to work! + +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $manager = new DisconnectedMetadataFactory($this->getContainer()->get('doctrine')); + + try { + $bundle = $this->getApplication()->getKernel()->getBundle($input->getArgument('name')); + + $output->writeln(sprintf('Generating entities for bundle "%s"', $bundle->getName())); + $metadata = $manager->getBundleMetadata($bundle); + } catch (\InvalidArgumentException $e) { + $name = strtr($input->getArgument('name'), '/', '\\'); + + if (false !== $pos = strpos($name, ':')) { + $name = $this->getContainer()->get('doctrine')->getEntityNamespace(substr($name, 0, $pos)).'\\'.substr($name, $pos + 1); + } + + if (class_exists($name)) { + $output->writeln(sprintf('Generating entity "%s"', $name)); + $metadata = $manager->getClassMetadata($name, $input->getOption('path')); + } else { + $output->writeln(sprintf('Generating entities for namespace "%s"', $name)); + $metadata = $manager->getNamespaceMetadata($name, $input->getOption('path')); + } + } + + $generator = $this->getEntityGenerator(); + + $backupExisting = !$input->getOption('no-backup'); + $generator->setBackupExisting($backupExisting); + + $repoGenerator = new EntityRepositoryGenerator(); + foreach ($metadata->getMetadata() as $m) { + if ($backupExisting) { + $basename = substr($m->name, strrpos($m->name, '\\') + 1); + $output->writeln(sprintf(' > backing up %s.php to %s.php~', $basename, $basename)); + } + // Getting the metadata for the entity class once more to get the correct path if the namespace has multiple occurrences + try { + $entityMetadata = $manager->getClassMetadata($m->getName(), $input->getOption('path')); + } catch (\RuntimeException $e) { + // fall back to the bundle metadata when no entity class could be found + $entityMetadata = $metadata; + } + + $output->writeln(sprintf(' > generating %s', $m->name)); + $generator->generate(array($m), $entityMetadata->getPath()); + + if ($m->customRepositoryClassName && false !== strpos($m->customRepositoryClassName, $metadata->getNamespace())) { + $repoGenerator->writeEntityRepositoryClass($m->customRepositoryClassName, $metadata->getPath()); + } + } + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/ImportMappingDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/ImportMappingDoctrineCommand.php new file mode 100644 index 0000000..38d664f --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/ImportMappingDoctrineCommand.php @@ -0,0 +1,126 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Mapping\Driver\DatabaseDriver; +use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; +use Doctrine\ORM\Tools\Export\ClassMetadataExporter; +use Doctrine\ORM\Tools\Console\MetadataFilter; + +/** + * Import Doctrine ORM metadata mapping information from an existing database. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class ImportMappingDoctrineCommand extends DoctrineCommand +{ + protected function configure() + { + $this + ->setName('doctrine:mapping:import') + ->addArgument('bundle', InputArgument::REQUIRED, 'The bundle to import the mapping information to') + ->addArgument('mapping-type', InputArgument::OPTIONAL, 'The mapping type to export the imported mapping information to') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->addOption('filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A string pattern used to match entities that should be mapped.') + ->addOption('force', null, InputOption::VALUE_NONE, 'Force to overwrite existing mapping files.') + ->setDescription('Imports mapping information from an existing database') + ->setHelp(<<doctrine:mapping:import command imports mapping information +from an existing database: + +php app/console doctrine:mapping:import "MyCustomBundle" xml + +You can also optionally specify which entity manager to import from with the +--em option: + +php app/console doctrine:mapping:import "MyCustomBundle" xml --em=default + +If you don't want to map every entity that can be found in the database, use the +--filter option. It will try to match the targeted mapped entity with the +provided pattern string. + +php app/console doctrine:mapping:import "MyCustomBundle" xml --filter=MyMatchedEntity + +Use the --force option, if you want to override existing mapping files: + +php app/console doctrine:mapping:import "MyCustomBundle" xml --force +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $bundle = $this->getApplication()->getKernel()->getBundle($input->getArgument('bundle')); + + $destPath = $bundle->getPath(); + $type = $input->getArgument('mapping-type') ? $input->getArgument('mapping-type') : 'xml'; + if ('annotation' === $type) { + $destPath .= '/Entity'; + } else { + $destPath .= '/Resources/config/doctrine'; + } + if ('yaml' === $type) { + $type = 'yml'; + } + + $cme = new ClassMetadataExporter(); + $exporter = $cme->getExporter($type); + $exporter->setOverwriteExistingFiles($input->getOption('force')); + + if ('annotation' === $type) { + $entityGenerator = $this->getEntityGenerator(); + $exporter->setEntityGenerator($entityGenerator); + } + + $em = $this->getEntityManager($input->getOption('em')); + + $databaseDriver = new DatabaseDriver($em->getConnection()->getSchemaManager()); + $em->getConfiguration()->setMetadataDriverImpl($databaseDriver); + + $emName = $input->getOption('em'); + $emName = $emName ? $emName : 'default'; + + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + $metadata = $cmf->getAllMetadata(); + $metadata = MetadataFilter::filter($metadata, $input->getOption('filter')); + if ($metadata) { + $output->writeln(sprintf('Importing mapping information from "%s" entity manager', $emName)); + foreach ($metadata as $class) { + $className = $class->name; + $class->name = $bundle->getNamespace().'\\Entity\\'.$className; + if ('annotation' === $type) { + $path = $destPath.'/'.$className.'.php'; + } else { + $path = $destPath.'/'.$className.'.orm.'.$type; + } + $output->writeln(sprintf(' > writing %s', $path)); + $code = $exporter->exportClassMetadata($class); + if (!is_dir($dir = dirname($path))) { + mkdir($dir, 0777, true); + } + file_put_contents($path, $code); + } + } else { + $output->writeln('Database does not have any mapping information.', 'ERROR'); + $output->writeln('', 'ERROR'); + } + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearMetadataCacheDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearMetadataCacheDoctrineCommand.php new file mode 100644 index 0000000..4764d14 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearMetadataCacheDoctrineCommand.php @@ -0,0 +1,58 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand; + +/** + * Command to clear the metadata cache of the various cache drivers. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class ClearMetadataCacheDoctrineCommand extends MetadataCommand +{ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:cache:clear-metadata') + ->setDescription('Clears all metadata cache for a entity manager') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->setHelp(<<doctrine:cache:clear-metadata command clears all metadata +cache for the default entity manager: + +php app/console doctrine:cache:clear-metadata + +You can also optionally specify the --em option to specify +which entity manager to clear the cache for: + +php app/console doctrine:cache:clear-metadata --em=default +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + return parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearQueryCacheDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearQueryCacheDoctrineCommand.php new file mode 100644 index 0000000..e45cef4 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearQueryCacheDoctrineCommand.php @@ -0,0 +1,58 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand; + +/** + * Command to clear the query cache of the various cache drivers. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class ClearQueryCacheDoctrineCommand extends QueryCommand +{ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:cache:clear-query') + ->setDescription('Clears all query cache for a entity manager') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->setHelp(<<doctrine:cache:clear-query command clears all query cache for +the default entity manager: + +php app/console doctrine:cache:clear-query + +You can also optionally specify the --em option to specify +which entity manager to clear the cache for: + +php app/console doctrine:cache:clear-query --em=default +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + return parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearResultCacheDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearResultCacheDoctrineCommand.php new file mode 100644 index 0000000..08c6e44 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ClearResultCacheDoctrineCommand.php @@ -0,0 +1,73 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand; + +/** + * Command to clear the result cache of the various cache drivers. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class ClearResultCacheDoctrineCommand extends ResultCommand +{ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:cache:clear-result') + ->setDescription('Clears result cache for a entity manager') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->setHelp(<<doctrine:cache:clear-result command clears all result cache +for the default entity manager: + +php app/console doctrine:cache:clear-result + +You can also optionally specify the --em option to specify +which entity manager to clear the cache for: + +php app/console doctrine:cache:clear-result --em=default + +If you don't want to clear all result cache you can specify some additional +options to control what cache is deleted: + +php app/console doctrine:cache:clear-result --id=cache_key + +Or you can specify a --regex to delete cache entries that +match it: + +php app/console doctrine:cache:clear-result --regex="user_(.*)" + +You can also specify a --prefix or +--suffix to delete cache entries for: + +php app/console doctrine:cache:clear-result --prefix="user_" --suffix="_frontend" +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + return parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ConvertMappingDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ConvertMappingDoctrineCommand.php new file mode 100644 index 0000000..796c091 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ConvertMappingDoctrineCommand.php @@ -0,0 +1,68 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand; +use Doctrine\ORM\Tools\Export\Driver\XmlExporter; +use Doctrine\ORM\Tools\Export\Driver\YamlExporter; + +/** + * Convert Doctrine ORM metadata mapping information between the various supported + * formats. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class ConvertMappingDoctrineCommand extends ConvertMappingCommand +{ + protected function configure() + { + parent::configure(); + $this + ->setName('doctrine:mapping:convert') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->setHelp(<<doctrine:mapping:convert command converts mapping information +between supported formats: + +php app/console doctrine:mapping:convert xml /path/to/output +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + return parent::execute($input, $output); + } + + + protected function getExporter($toType, $destPath) + { + $exporter = parent::getExporter($toType, $destPath); + if ($exporter instanceof XmlExporter) { + $exporter->setExtension('.orm.xml'); + } elseif ($exporter instanceof YamlExporter) { + $exporter->setExtension('.orm.yml'); + } + + return $exporter; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/CreateSchemaDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/CreateSchemaDoctrineCommand.php new file mode 100644 index 0000000..f2ccf91 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/CreateSchemaDoctrineCommand.php @@ -0,0 +1,63 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand; + +/** + * Command to execute the SQL needed to generate the database schema for + * a given entity manager. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class CreateSchemaDoctrineCommand extends CreateCommand +{ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:schema:create') + ->setDescription('Executes (or dumps) the SQL needed to generate the database schema') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->setHelp(<<doctrine:schema:create command executes the SQL needed to +generate the database schema for the default entity manager: + +php app/console doctrine:schema:create + +You can also generate the database schema for a specific entity manager: + +php app/console doctrine:schema:create --em=default + +Finally, instead of executing the SQL, you can output the SQL: + +php app/console doctrine:schema:create --dump-sql +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/DoctrineCommandHelper.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/DoctrineCommandHelper.php new file mode 100644 index 0000000..2a440bf --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/DoctrineCommandHelper.php @@ -0,0 +1,48 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Application; +use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper; +use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper; + +/** + * Provides some helper and convenience methods to configure doctrine commands in the context of bundles + * and multiple connections/entity managers. + * + * @author Fabien Potencier + */ +abstract class DoctrineCommandHelper +{ + /** + * Convenience method to push the helper sets of a given entity manager into the application. + * + * @param Application $application + * @param string $emName + */ + static public function setApplicationEntityManager(Application $application, $emName) + { + $em = $application->getKernel()->getContainer()->get('doctrine')->getManager($emName); + $helperSet = $application->getHelperSet(); + $helperSet->set(new ConnectionHelper($em->getConnection()), 'db'); + $helperSet->set(new EntityManagerHelper($em), 'em'); + } + + static public function setApplicationConnection(Application $application, $connName) + { + $connection = $application->getKernel()->getContainer()->get('doctrine')->getConnection($connName); + $helperSet = $application->getHelperSet(); + $helperSet->set(new ConnectionHelper($connection), 'db'); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/DropSchemaDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/DropSchemaDoctrineCommand.php new file mode 100644 index 0000000..9746f28 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/DropSchemaDoctrineCommand.php @@ -0,0 +1,63 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand; + +/** + * Command to drop the database schema for a set of classes based on their mappings. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class DropSchemaDoctrineCommand extends DropCommand +{ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:schema:drop') + ->setDescription('Executes (or dumps) the SQL needed to drop the current database schema') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->setHelp(<<doctrine:schema:drop command generates the SQL needed to +drop the database schema of the default entity manager: + +php app/console doctrine:schema:drop --dump-sql + +Alternatively, you can execute the generated queries: + +php app/console doctrine:schema:drop --force + +You can also optionally specify the name of a entity manager to drop the +schema for: + +php app/console doctrine:schema:drop --em=default +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/EnsureProductionSettingsDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/EnsureProductionSettingsDoctrineCommand.php new file mode 100644 index 0000000..c2a8024 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/EnsureProductionSettingsDoctrineCommand.php @@ -0,0 +1,58 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand; + +/** + * Ensure the Doctrine ORM is configured properly for a production environment. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class EnsureProductionSettingsDoctrineCommand extends EnsureProductionSettingsCommand +{ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:ensure-production-settings') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->setHelp(<<doctrine:ensure-production-settings command ensures that +Doctrine is properly configured for a production environment.: + +php app/console doctrine:ensure-production-settings + +You can also optionally specify the --em option to specify +which entity manager to use: + +php app/console doctrine:ensure-production-settings --em=default +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/InfoDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/InfoDoctrineCommand.php new file mode 100644 index 0000000..72db01a --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/InfoDoctrineCommand.php @@ -0,0 +1,57 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\Tools\Console\Command\InfoCommand; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Show information about mapped entities + * + * @author Benjamin Eberlei + */ +class InfoDoctrineCommand extends InfoCommand +{ + protected function configure() + { + $this + ->setName('doctrine:mapping:info') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->setDescription('Shows basic information about all mapped entities') + ->setHelp(<<doctrine:mapping:info shows basic information about which +entities exist and possibly if their mapping information contains errors or +not. + +php app/console doctrine:mapping:info + +If you are using multiple entity managers you can pick your choice with the +--em option: + +php app/console doctrine:mapping:info --em=default +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/RunDqlDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/RunDqlDoctrineCommand.php new file mode 100644 index 0000000..eaff6bb --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/RunDqlDoctrineCommand.php @@ -0,0 +1,63 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Doctrine\ORM\Tools\Console\Command\RunDqlCommand; + +/** + * Execute a Doctrine DQL query and output the results. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class RunDqlDoctrineCommand extends RunDqlCommand +{ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:query:dql') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->setHelp(<<doctrine:query:dql command executes the given DQL query and +outputs the results: + +php app/console doctrine:query:dql "SELECT u FROM UserBundle:User u" + +You can also optional specify some additional options like what type of +hydration to use when executing the query: + +php app/console doctrine:query:dql "SELECT u FROM UserBundle:User u" --hydrate=array + +Additionally you can specify the first result and maximum amount of results to +show: + +php app/console doctrine:query:dql "SELECT u FROM UserBundle:User u" --first-result=0 --max-result=30 +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + return parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/RunSqlDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/RunSqlDoctrineCommand.php new file mode 100644 index 0000000..e04f44e --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/RunSqlDoctrineCommand.php @@ -0,0 +1,53 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Doctrine\DBAL\Tools\Console\Command\RunSqlCommand; + +/** + * Execute a SQL query and output the results. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class RunSqlDoctrineCommand extends RunSqlCommand +{ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:query:sql') + ->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'The connection to use for this command') + ->setHelp(<<doctrine:query:sql command executes the given DQL query and +outputs the results: + +php app/console doctrine:query:sql "SELECT * from user" +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationConnection($this->getApplication(), $input->getOption('connection')); + + return parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/UpdateSchemaDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/UpdateSchemaDoctrineCommand.php new file mode 100644 index 0000000..4d7f0d2 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/UpdateSchemaDoctrineCommand.php @@ -0,0 +1,67 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand; + +/** + * Command to generate the SQL needed to update the database schema to match + * the current mapping information. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class UpdateSchemaDoctrineCommand extends UpdateCommand +{ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:schema:update') + ->setDescription('Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->setHelp(<<doctrine:schema:update command generates the SQL needed to +synchronize the database schema with the current mapping metadata of the +default entity manager. + +For example, if you add metadata for a new column to an entity, this command +would generate and output the SQL needed to add the new column to the database: + +php app/console doctrine:schema:update --dump-sql + +Alternatively, you can execute the generated queries: + +php app/console doctrine:schema:update --force + +You can also update the database schema for a specific entity manager: + +php app/console doctrine:schema:update --em=default +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ValidateSchemaCommand.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ValidateSchemaCommand.php new file mode 100644 index 0000000..a20a9dc --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Command/Proxy/ValidateSchemaCommand.php @@ -0,0 +1,59 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand as DoctrineValidateSchemaCommand; + +/** + * Command to run Doctrine ValidateSchema() on the current mappings. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + * @author Neil Katin + */ +class ValidateSchemaCommand extends DoctrineValidateSchemaCommand +{ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:schema:validate') + ->setDescription('Validates the doctrine mapping files') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->setHelp(<<doctrine:schema:validate checks the current mappings +for valid forward and reverse mappings. + +php app/console doctrine:schema:validate + +You can also optionally specify the --em option to specify +which entity manager use for the validation. + +php app/console doctrine:schema:validate --em=default +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + return parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/ConnectionFactory.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/ConnectionFactory.php new file mode 100644 index 0000000..29d04f2 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/ConnectionFactory.php @@ -0,0 +1,85 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle; + +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Types\Type; + +/** + * Connection + */ +class ConnectionFactory +{ + private $typesConfig = array(); + private $commentedTypes = array(); + private $initialized = false; + + /** + * Construct. + * + * @param array $typesConfig + */ + public function __construct(array $typesConfig) + { + $this->typesConfig = $typesConfig; + } + + /** + * Create a connection by name. + * + * @param array $params + * @param Configuration $config + * @param EventManager $eventManager + * + * @return \Doctrine\DBAL\Connection + */ + public function createConnection(array $params, Configuration $config = null, EventManager $eventManager = null, array $mappingTypes = array()) + { + if (!$this->initialized) { + $this->initializeTypes(); + $this->initialized = true; + } + + $connection = DriverManager::getConnection($params, $config, $eventManager); + + if (!empty($mappingTypes)) { + $platform = $connection->getDatabasePlatform(); + foreach ($mappingTypes as $dbType => $doctrineType) { + $platform->registerDoctrineTypeMapping($dbType, $doctrineType); + } + foreach ($this->commentedTypes as $type) { + $platform->markDoctrineTypeCommented(Type::getType($type)); + } + } + + return $connection; + } + + private function initializeTypes() + { + foreach ($this->typesConfig as $type => $typeConfig) { + if (Type::hasType($type)) { + Type::overrideType($type, $typeConfig['class']); + } else { + Type::addType($type, $typeConfig['class']); + } + if ($typeConfig['commented']) { + $this->commentedTypes[] = $type; + } + } + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Controller/ProfilerController.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Controller/ProfilerController.php new file mode 100644 index 0000000..66009e0 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Controller/ProfilerController.php @@ -0,0 +1,67 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAware; +use Symfony\Component\HttpFoundation\Response; + +/** + * ProfilerController. + * + * @author Christophe Coevoet + */ +class ProfilerController extends ContainerAware +{ + /** + * Renders the profiler panel for the given token. + * + * @param string $token The profiler token + * @param string $connectionName + * @param integer $query + * + * @return Response A Response instance + */ + public function explainAction($token, $connectionName, $query) + { + $profiler = $this->container->get('profiler'); + $profiler->disable(); + + $profile = $profiler->loadProfile($token); + $queries = $profile->getCollector('db')->getQueries(); + + if (!isset($queries[$connectionName][$query])) { + return new Response('This query does not exist.'); + } + + $query = $queries[$connectionName][$query]; + if (!$query['explainable']) { + return new Response('This query cannot be explained.'); + } + + /** @var $connection \Doctrine\DBAL\Connection */ + $connection = $this->container->get('doctrine')->getConnection($connectionName); + try { + $results = $connection->executeQuery('EXPLAIN '.$query['sql'], $query['params'], $query['types']) + ->fetchAll(\PDO::FETCH_ASSOC); + } catch (\Exception $e) { + return new Response('This query cannot be explained.'); + } + + return $this->container->get('templating')->renderResponse('DoctrineBundle:Collector:explain.html.twig', array( + 'data' => $results, + 'query' => $query, + )); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DependencyInjection/Configuration.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..e10b484 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DependencyInjection/Configuration.php @@ -0,0 +1,388 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * This class contains the configuration information for the bundle + * + * This information is solely responsible for how the different configuration + * sections are normalized, and merged. + * + * @author Christophe Coevoet + */ +class Configuration implements ConfigurationInterface +{ + private $debug; + + /** + * Constructor + * + * @param Boolean $debug Whether to use the debug mode + */ + public function __construct($debug) + { + $this->debug = (Boolean) $debug; + } + + /** + * Generates the configuration tree builder. + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('doctrine'); + + $this->addDbalSection($rootNode); + $this->addOrmSection($rootNode); + + return $treeBuilder; + } + + private function addDbalSection(ArrayNodeDefinition $node) + { + $node + ->children() + ->arrayNode('dbal') + ->beforeNormalization() + ->ifTrue(function ($v) { return is_array($v) && !array_key_exists('connections', $v) && !array_key_exists('connection', $v); }) + ->then(function ($v) { + // Key that should not be rewritten to the connection config + $excludedKeys = array('default_connection' => true, 'types' => true, 'type' => true); + $connection = array(); + foreach ($v as $key => $value) { + if (isset($excludedKeys[$key])) { + continue; + } + $connection[$key] = $v[$key]; + unset($v[$key]); + } + $v['default_connection'] = isset($v['default_connection']) ? (string) $v['default_connection'] : 'default'; + $v['connections'] = array($v['default_connection'] => $connection); + + return $v; + }) + ->end() + ->children() + ->scalarNode('default_connection')->end() + ->end() + ->fixXmlConfig('type') + ->children() + ->arrayNode('types') + ->useAttributeAsKey('name') + ->prototype('array') + ->beforeNormalization() + ->ifString() + ->then(function($v) { return array('class' => $v); }) + ->end() + ->children() + ->scalarNode('class')->isRequired()->end() + ->booleanNode('commented')->defaultTrue()->end() + ->end() + ->end() + ->end() + ->end() + ->fixXmlConfig('connection') + ->append($this->getDbalConnectionsNode()) + ->end() + ; + } + + private function getDbalConnectionsNode() + { + $treeBuilder = new TreeBuilder(); + $node = $treeBuilder->root('connections'); + + /** @var $connectionNode ArrayNodeDefinition */ + $connectionNode = $node + ->requiresAtLeastOneElement() + ->useAttributeAsKey('name') + ->prototype('array') + ; + + $this->configureDbalDriverNode($connectionNode); + + $connectionNode + ->fixXmlConfig('option') + ->fixXmlConfig('mapping_type') + ->fixXmlConfig('slave') + ->children() + ->scalarNode('driver')->defaultValue('pdo_mysql')->end() + ->scalarNode('platform_service')->end() + ->booleanNode('logging')->defaultValue($this->debug)->end() + ->booleanNode('profiling')->defaultValue($this->debug)->end() + ->scalarNode('driver_class')->end() + ->scalarNode('wrapper_class')->end() + ->arrayNode('options') + ->useAttributeAsKey('key') + ->prototype('scalar')->end() + ->end() + ->arrayNode('mapping_types') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->end() + ; + + $slaveNode = $connectionNode + ->children() + ->arrayNode('slaves') + ->useAttributeAsKey('name') + ->prototype('array') + ; + $this->configureDbalDriverNode($slaveNode); + + return $node; + } + + /** + * Adds config keys related to params processed by the DBAL drivers + * + * These keys are available for slave configurations too. + * + * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $node + */ + private function configureDbalDriverNode(ArrayNodeDefinition $node) + { + $node + ->children() + ->scalarNode('dbname')->end() + ->scalarNode('host')->defaultValue('localhost')->end() + ->scalarNode('port')->defaultNull()->end() + ->scalarNode('user')->defaultValue('root')->end() + ->scalarNode('password')->defaultNull()->end() + ->scalarNode('charset')->end() + ->scalarNode('path')->end() + ->booleanNode('memory')->end() + ->scalarNode('unix_socket')->info('The unix socket to use for MySQL')->end() + ->booleanNode('persistent')->info('True to use as persistent connection for the ibm_db2 driver')->end() + ->scalarNode('protocol')->info('The protocol to use for the ibm_db2 driver (default to TCPIP if ommited)')->end() + ->booleanNode('service')->info('True to use dbname as service name instead of SID for Oracle')->end() + ->scalarNode('sessionMode') + ->info('The session mode to use for the oci8 driver') + ->end() + ->booleanNode('pooled')->info('True to use a pooled server with the oci8 driver')->end() + ->booleanNode('MultipleActiveResultSets')->info('Configuring MultipleActiveResultSets for the pdo_sqlsrv driver')->end() + ->end() + ->beforeNormalization() + ->ifTrue(function($v) {return !isset($v['sessionMode']) && isset($v['session_mode']);}) + ->then(function($v) { + $v['sessionMode'] = $v['session_mode']; + unset($v['session_mode']); + + return $v; + }) + ->end() + ->beforeNormalization() + ->ifTrue(function($v) {return !isset($v['MultipleActiveResultSets']) && isset($v['multiple_active_result_sets']);}) + ->then(function($v) { + $v['MultipleActiveResultSets'] = $v['multiple_active_result_sets']; + unset($v['multiple_active_result_sets']); + + return $v; + }) + ->end() + ; + } + + private function addOrmSection(ArrayNodeDefinition $node) + { + $node + ->children() + ->arrayNode('orm') + ->beforeNormalization() + ->ifTrue(function ($v) { return null === $v || (is_array($v) && !array_key_exists('entity_managers', $v) && !array_key_exists('entity_manager', $v)); }) + ->then(function ($v) { + $v = (array) $v; + // Key that should not be rewritten to the connection config + $excludedKeys = array( + 'default_entity_manager' => true, 'auto_generate_proxy_classes' => true, + 'proxy_dir' => true, 'proxy_namespace' => true, 'resolve_target_entities' => true, + 'resolve_target_entity' => true, + ); + $entityManager = array(); + foreach ($v as $key => $value) { + if (isset($excludedKeys[$key])) { + continue; + } + $entityManager[$key] = $v[$key]; + unset($v[$key]); + } + $v['default_entity_manager'] = isset($v['default_entity_manager']) ? (string) $v['default_entity_manager'] : 'default'; + $v['entity_managers'] = array($v['default_entity_manager'] => $entityManager); + + return $v; + }) + ->end() + ->children() + ->scalarNode('default_entity_manager')->end() + ->booleanNode('auto_generate_proxy_classes')->defaultFalse()->end() + ->scalarNode('proxy_dir')->defaultValue('%kernel.cache_dir%/doctrine/orm/Proxies')->end() + ->scalarNode('proxy_namespace')->defaultValue('Proxies')->end() + ->end() + ->fixXmlConfig('entity_manager') + ->append($this->getOrmEntityManagersNode()) + ->fixXmlConfig('resolve_target_entity', 'resolve_target_entities') + ->append($this->getOrmTargetEntityResolverNode()) + ->end() + ->end() + ; + } + + private function getOrmTargetEntityResolverNode() + { + $treeBuilder = new TreeBuilder(); + $node = $treeBuilder->root('resolve_target_entities'); + + $node + ->useAttributeAsKey('interface') + ->prototype('scalar') + ->cannotBeEmpty() + ->end() + ; + + return $node; + } + + private function getOrmEntityManagersNode() + { + $treeBuilder = new TreeBuilder(); + $node = $treeBuilder->root('entity_managers'); + + $node + ->requiresAtLeastOneElement() + ->useAttributeAsKey('name') + ->prototype('array') + ->addDefaultsIfNotSet() + ->append($this->getOrmCacheDriverNode('query_cache_driver')) + ->append($this->getOrmCacheDriverNode('metadata_cache_driver')) + ->append($this->getOrmCacheDriverNode('result_cache_driver')) + ->children() + ->scalarNode('connection')->end() + ->scalarNode('class_metadata_factory_name')->defaultValue('Doctrine\ORM\Mapping\ClassMetadataFactory')->end() + ->scalarNode('default_repository_class')->defaultValue('Doctrine\ORM\EntityRepository')->end() + ->scalarNode('auto_mapping')->defaultFalse()->end() + ->end() + ->fixXmlConfig('hydrator') + ->children() + ->arrayNode('hydrators') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->end() + ->fixXmlConfig('mapping') + ->children() + ->arrayNode('mappings') + ->useAttributeAsKey('name') + ->prototype('array') + ->beforeNormalization() + ->ifString() + ->then(function($v) { return array('type' => $v); }) + ->end() + ->treatNullLike(array()) + ->treatFalseLike(array('mapping' => false)) + ->performNoDeepMerging() + ->children() + ->scalarNode('mapping')->defaultValue(true)->end() + ->scalarNode('type')->end() + ->scalarNode('dir')->end() + ->scalarNode('alias')->end() + ->scalarNode('prefix')->end() + ->booleanNode('is_bundle')->end() + ->end() + ->end() + ->end() + ->arrayNode('dql') + ->fixXmlConfig('string_function') + ->fixXmlConfig('numeric_function') + ->fixXmlConfig('datetime_function') + ->children() + ->arrayNode('string_functions') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->arrayNode('numeric_functions') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->arrayNode('datetime_functions') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->fixXmlConfig('filter') + ->children() + ->arrayNode('filters') + ->info('Register SQL Filters in the entity manager') + ->useAttributeAsKey('name') + ->prototype('array') + ->beforeNormalization() + ->ifString() + ->then(function($v) { return array('class' => $v); }) + ->end() + ->beforeNormalization() + // The content of the XML node is returned as the "value" key so we need to rename it + ->ifTrue(function($v) {return is_array($v) && isset($v['value']); }) + ->then(function($v) { + $v['class'] = $v['value']; + unset($v['value']); + + return $v; + }) + ->end() + ->children() + ->scalarNode('class')->isRequired()->end() + ->booleanNode('enabled')->defaultFalse()->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + + return $node; + } + + private function getOrmCacheDriverNode($name) + { + $treeBuilder = new TreeBuilder(); + $node = $treeBuilder->root($name); + + $node + ->addDefaultsIfNotSet() + ->beforeNormalization() + ->ifString() + ->then(function($v) { return array('type' => $v); }) + ->end() + ->children() + ->scalarNode('type')->defaultValue('array')->end() + ->scalarNode('host')->end() + ->scalarNode('port')->end() + ->scalarNode('instance_class')->end() + ->scalarNode('class')->end() + ->scalarNode('id')->end() + ->end() + ; + + return $node; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php new file mode 100644 index 0000000..224527c --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DependencyInjection/DoctrineExtension.php @@ -0,0 +1,437 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension; +use Symfony\Component\Config\FileLocator; + +/** + * DoctrineExtension is an extension for the Doctrine DBAL and ORM library. + * + * @author Jonathan H. Wage + * @author Fabien Potencier + * @author Benjamin Eberlei + */ +class DoctrineExtension extends AbstractDoctrineExtension +{ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + if (!empty($config['dbal'])) { + $this->dbalLoad($config['dbal'], $container); + } + + if (!empty($config['orm'])) { + $this->ormLoad($config['orm'], $container); + } + } + + /** + * Loads the DBAL configuration. + * + * Usage example: + * + * + * + * @param array $config An array of configuration settings + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function dbalLoad(array $config, ContainerBuilder $container) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('dbal.xml'); + + if (empty($config['default_connection'])) { + $keys = array_keys($config['connections']); + $config['default_connection'] = reset($keys); + } + $this->defaultConnection = $config['default_connection']; + + $container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection)); + $container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false)); + + $container->setParameter('doctrine.dbal.connection_factory.types', $config['types']); + + $connections = array(); + foreach (array_keys($config['connections']) as $name) { + $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name); + } + $container->setParameter('doctrine.connections', $connections); + $container->setParameter('doctrine.default_connection', $this->defaultConnection); + + foreach ($config['connections'] as $name => $connection) { + $this->loadDbalConnection($name, $connection, $container); + } + } + + /** + * Loads a configured DBAL connection. + * + * @param string $name The name of the connection + * @param array $connection A dbal connection configuration. + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function loadDbalConnection($name, array $connection, ContainerBuilder $container) + { + // configuration + $configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new DefinitionDecorator('doctrine.dbal.connection.configuration')); + $logger = null; + if ($connection['logging']) { + $logger = new Reference('doctrine.dbal.logger'); + } + unset ($connection['logging']); + if ($connection['profiling']) { + $profilingLoggerId = 'doctrine.dbal.logger.profiling.'.$name; + $container->setDefinition($profilingLoggerId, new DefinitionDecorator('doctrine.dbal.logger.profiling')); + $logger = new Reference($profilingLoggerId); + $container->getDefinition('data_collector.doctrine')->addMethodCall('addLogger', array($name, $logger)); + + if (null !== $logger) { + $chainLogger = new DefinitionDecorator('doctrine.dbal.logger.chain'); + $chainLogger->addMethodCall('addLogger', array($logger)); + + $loggerId = 'doctrine.dbal.logger.chain.'.$name; + $container->setDefinition($loggerId, $chainLogger); + $logger = new Reference($loggerId); + } + } + unset($connection['profiling']); + + if ($logger) { + $configuration->addMethodCall('setSQLLogger', array($logger)); + } + + // event manager + $def = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new DefinitionDecorator('doctrine.dbal.connection.event_manager')); + + // connection + if (isset($connection['charset'])) { + if ((isset($connection['driver']) && stripos($connection['driver'], 'mysql') !== false) || + (isset($connection['driver_class']) && stripos($connection['driver_class'], 'mysql') !== false)) { + $mysqlSessionInit = new Definition('%doctrine.dbal.events.mysql_session_init.class%'); + $mysqlSessionInit->setArguments(array($connection['charset'])); + $mysqlSessionInit->setPublic(false); + $mysqlSessionInit->addTag('doctrine.event_subscriber', array('connection' => $name)); + + $container->setDefinition( + sprintf('doctrine.dbal.%s_connection.events.mysqlsessioninit', $name), + $mysqlSessionInit + ); + unset($connection['charset']); + } + } + + $options = $this->getConnectionOptions($connection); + + $container + ->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new DefinitionDecorator('doctrine.dbal.connection')) + ->setArguments(array( + $options, + new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)), + new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)), + $connection['mapping_types'], + )) + ; + } + + protected function getConnectionOptions($connection) + { + $options = $connection; + + if (isset($options['platform_service'])) { + $options['platform'] = new Reference($options['platform_service']); + unset($options['platform_service']); + } + unset($options['mapping_types']); + + foreach (array( + 'options' => 'driverOptions', + 'driver_class' => 'driverClass', + 'wrapper_class' => 'wrapperClass', + ) as $old => $new) { + if (isset($options[$old])) { + $options[$new] = $options[$old]; + unset($options[$old]); + } + } + + if (!empty($options['slaves'])) { + $nonRewrittenKeys = array( + 'driver' => true, 'driverOptions' => true, 'driverClass' => true, 'wrapperClass' => true, + 'platform' => true, 'slaves' => true, 'master' => true, + // included by safety but should have been unset already + 'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true, + ); + foreach ($options as $key => $value) { + if (isset($nonRewrittenKeys[$key])) { + continue; + } + $options['master'][$key] = $value; + unset($options[$key]); + } + if (empty($options['wrapperClass'])) { + // Change the wrapper class only if the user does not already forced using a custom one. + $options['wrapperClass'] = 'Doctrine\\DBAL\\Connections\\MasterSlaveConnection'; + } + } else { + unset($options['slaves']); + } + + return $options; + } + + /** + * Loads the Doctrine ORM configuration. + * + * Usage example: + * + * + * + * @param array $config An array of configuration settings + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function ormLoad(array $config, ContainerBuilder $container) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('orm.xml'); + + $this->entityManagers = array(); + foreach (array_keys($config['entity_managers']) as $name) { + $this->entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name); + } + $container->setParameter('doctrine.entity_managers', $this->entityManagers); + + if (empty($config['default_entity_manager'])) { + $tmp = array_keys($this->entityManagers); + $config['default_entity_manager'] = reset($tmp); + } + $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']); + + $options = array('auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace'); + foreach ($options as $key) { + $container->setParameter('doctrine.orm.'.$key, $config[$key]); + } + + $container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager'])); + + foreach ($config['entity_managers'] as $name => $entityManager) { + $entityManager['name'] = $name; + $this->loadOrmEntityManager($entityManager, $container); + } + + if ($config['resolve_target_entities']) { + $def = $container->findDefinition('doctrine.orm.listeners.resolve_target_entity'); + foreach ($config['resolve_target_entities'] as $name => $implementation) { + $def->addMethodCall('addResolveTargetEntity', array( + $name, $implementation, array() + )); + } + + $def->addTag('doctrine.event_listener', array('event' => 'loadClassMetadata')); + } + } + + /** + * Loads a configured ORM entity manager. + * + * @param array $entityManager A configured ORM entity manager. + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function loadOrmEntityManager(array $entityManager, ContainerBuilder $container) + { + if ($entityManager['auto_mapping'] && count($this->entityManagers) > 1) { + throw new \LogicException('You cannot enable "auto_mapping" when several entity managers are defined.'); + } + + $ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new DefinitionDecorator('doctrine.orm.configuration')); + + $this->loadOrmEntityManagerMappingInformation($entityManager, $ormConfigDef, $container); + $this->loadOrmCacheDrivers($entityManager, $container); + + $methods = array( + 'setMetadataCacheImpl' => new Reference(sprintf('doctrine.orm.%s_metadata_cache', $entityManager['name'])), + 'setQueryCacheImpl' => new Reference(sprintf('doctrine.orm.%s_query_cache', $entityManager['name'])), + 'setResultCacheImpl' => new Reference(sprintf('doctrine.orm.%s_result_cache', $entityManager['name'])), + 'setMetadataDriverImpl' => new Reference('doctrine.orm.'.$entityManager['name'].'_metadata_driver'), + 'setProxyDir' => '%doctrine.orm.proxy_dir%', + 'setProxyNamespace' => '%doctrine.orm.proxy_namespace%', + 'setAutoGenerateProxyClasses' => '%doctrine.orm.auto_generate_proxy_classes%', + 'setClassMetadataFactoryName' => $entityManager['class_metadata_factory_name'], + 'setDefaultRepositoryClassName' => $entityManager['default_repository_class'], + ); + foreach ($methods as $method => $arg) { + $ormConfigDef->addMethodCall($method, array($arg)); + } + + foreach ($entityManager['hydrators'] as $name => $class) { + $ormConfigDef->addMethodCall('addCustomHydrationMode', array($name, $class)); + } + + if (!empty($entityManager['dql'])) { + foreach ($entityManager['dql']['string_functions'] as $name => $function) { + $ormConfigDef->addMethodCall('addCustomStringFunction', array($name, $function)); + } + foreach ($entityManager['dql']['numeric_functions'] as $name => $function) { + $ormConfigDef->addMethodCall('addCustomNumericFunction', array($name, $function)); + } + foreach ($entityManager['dql']['datetime_functions'] as $name => $function) { + $ormConfigDef->addMethodCall('addCustomDatetimeFunction', array($name, $function)); + } + } + + $enabledFilters = array(); + foreach ($entityManager['filters'] as $name => $filter) { + $ormConfigDef->addMethodCall('addFilter', array($name, $filter['class'])); + if ($filter['enabled']) { + $enabledFilters[] = $name; + } + } + + $managerConfiguratorName = sprintf('doctrine.orm.%s_manager_configurator', $entityManager['name']); + $managerConfiguratorDef = $container + ->setDefinition($managerConfiguratorName, new DefinitionDecorator('doctrine.orm.manager_configurator.abstract')) + ->replaceArgument(0, $enabledFilters) + ; + + if (!isset($entityManager['connection'])) { + $entityManager['connection'] = $this->defaultConnection; + } + + $container + ->setDefinition(sprintf('doctrine.orm.%s_entity_manager', $entityManager['name']), new DefinitionDecorator('doctrine.orm.entity_manager.abstract')) + ->setArguments(array( + new Reference(sprintf('doctrine.dbal.%s_connection', $entityManager['connection'])), + new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])) + )) + ->setConfigurator(array(new Reference($managerConfiguratorName), 'configure')) + ; + + $container->setAlias( + sprintf('doctrine.orm.%s_entity_manager.event_manager', $entityManager['name']), + new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $entityManager['connection']), false) + ); + } + + /** + * Loads an ORM entity managers bundle mapping information. + * + * There are two distinct configuration possibilities for mapping information: + * + * 1. Specify a bundle and optionally details where the entity and mapping information reside. + * 2. Specify an arbitrary mapping location. + * + * @example + * + * doctrine.orm: + * mappings: + * MyBundle1: ~ + * MyBundle2: yml + * MyBundle3: { type: annotation, dir: Entities/ } + * MyBundle4: { type: xml, dir: Resources/config/doctrine/mapping } + * MyBundle5: + * type: yml + * dir: [bundle-mappings1/, bundle-mappings2/] + * alias: BundleAlias + * arbitrary_key: + * type: xml + * dir: %kernel.dir%/../src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entities + * prefix: DoctrineExtensions\Entities\ + * alias: DExt + * + * In the case of bundles everything is really optional (which leads to autodetection for this bundle) but + * in the mappings key everything except alias is a required argument. + * + * @param array $entityManager A configured ORM entity manager + * @param Definition $ormConfigDef A Definition instance + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function loadOrmEntityManagerMappingInformation(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container) + { + // reset state of drivers and alias map. They are only used by this methods and children. + $this->drivers = array(); + $this->aliasMap = array(); + + $this->loadMappingInformation($entityManager, $container); + $this->registerMappingDrivers($entityManager, $container); + + $ormConfigDef->addMethodCall('setEntityNamespaces', array($this->aliasMap)); + } + + protected function getObjectManagerElementName($name) + { + return 'doctrine.orm.'.$name; + } + + protected function getMappingObjectDefaultName() + { + return 'Entity'; + } + + protected function getMappingResourceConfigDirectory() + { + return 'Resources/config/doctrine'; + } + + protected function getMappingResourceExtension() + { + return 'orm'; + } + + /** + * Loads a configured entity managers cache drivers. + * + * @param array $entityManager A configured ORM entity manager. + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function loadOrmCacheDrivers(array $entityManager, ContainerBuilder $container) + { + $this->loadObjectManagerCacheDriver($entityManager, $container, 'metadata_cache'); + $this->loadObjectManagerCacheDriver($entityManager, $container, 'result_cache'); + $this->loadObjectManagerCacheDriver($entityManager, $container, 'query_cache'); + } + + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + */ + public function getXsdValidationBasePath() + { + return __DIR__.'/../Resources/config/schema'; + } + + /** + * Returns the namespace to be used for this extension (XML namespace). + * + * @return string The XML namespace + */ + public function getNamespace() + { + return 'http://symfony.com/schema/dic/doctrine'; + } + + public function getConfiguration(array $config, ContainerBuilder $container) + { + return new Configuration($container->getParameter('kernel.debug')); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DoctrineBundle.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DoctrineBundle.php new file mode 100644 index 0000000..d611572 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DoctrineBundle.php @@ -0,0 +1,120 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle; + +use Doctrine\Common\Util\ClassUtils; +use Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand; +use Doctrine\Bundle\DoctrineBundle\Command\DropDatabaseDoctrineCommand; +use Doctrine\Bundle\DoctrineBundle\Command\Proxy\RunSqlDoctrineCommand; +use Symfony\Component\Console\Application; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\DoctrineValidationPass; +use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass; +use Symfony\Bridge\Doctrine\DependencyInjection\Security\UserProvider\EntityFactory; + +/** + * Bundle. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class DoctrineBundle extends Bundle +{ + private $autoloader; + + public function build(ContainerBuilder $container) + { + parent::build($container); + + $container->addCompilerPass(new RegisterEventListenersAndSubscribersPass('doctrine.connections', 'doctrine.dbal.%s_connection.event_manager', 'doctrine'), PassConfig::TYPE_BEFORE_OPTIMIZATION); + + if ($container->hasExtension('security')) { + $container->getExtension('security')->addUserProviderFactory(new EntityFactory('entity', 'doctrine.orm.security.user.provider')); + } + $container->addCompilerPass(new DoctrineValidationPass('orm')); + } + + public function boot() + { + // Register an autoloader for proxies to avoid issues when unserializing them + // when the ORM is used. + if ($this->container->hasParameter('doctrine.orm.proxy_namespace')) { + $namespace = $this->container->getParameter('doctrine.orm.proxy_namespace'); + $dir = $this->container->getParameter('doctrine.orm.proxy_dir'); + // See https://github.com/symfony/symfony/pull/3419 for usage of + // references + $container =& $this->container; + + $this->autoloader = function($class) use ($namespace, $dir, &$container) { + if (0 === strpos($class, $namespace)) { + $fileName = str_replace('\\', '', substr($class, strlen($namespace) +1)); + $file = $dir.DIRECTORY_SEPARATOR.$fileName.'.php'; + + if (!is_file($file) && $container->getParameter('kernel.debug')) { + $originalClassName = ClassUtils::getRealClass($class); + $registry = $container->get('doctrine'); + + // Tries to auto-generate the proxy file + foreach ($registry->getManagers() as $em) { + + if ($em->getConfiguration()->getAutoGenerateProxyClasses()) { + $classes = $em->getMetadataFactory()->getAllMetadata(); + + foreach ($classes as $classMetadata) { + if ($classMetadata->name == $originalClassName) { + $em->getProxyFactory()->generateProxyClasses(array($classMetadata)); + } + } + } + } + + clearstatcache($file); + } + + if (file_exists($file)) { + require $file; + } + } + }; + spl_autoload_register($this->autoloader); + } + } + + public function shutdown() + { + if (null !== $this->autoloader) { + spl_autoload_unregister($this->autoloader); + $this->autoloader = null; + } + } + + public function registerCommands(Application $application) + { + // Use the default logic when the ORM is available. + // This avoids listing all ORM commands by hand. + if (class_exists('Doctrine\\ORM\\Version')) { + parent::registerCommands($application); + + return; + } + + // Register only the DBAL commands if the ORM is not available. + $application->add(new CreateDatabaseDoctrineCommand()); + $application->add(new DropDatabaseDoctrineCommand()); + $application->add(new RunSqlDoctrineCommand()); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/LICENSE b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/LICENSE new file mode 100644 index 0000000..655a5ce --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2011 Fabien Potencier, Doctrine Project + +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/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/ManagerConfigurator.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/ManagerConfigurator.php new file mode 100644 index 0000000..fa4b563 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/ManagerConfigurator.php @@ -0,0 +1,59 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle; + +use Doctrine\ORM\EntityManager; + +/** + * Configurator for an EntityManager + * + * @author Christophe Coevoet + */ +class ManagerConfigurator +{ + private $enabledFilters = array(); + + /** + * Construct. + * + * @param array $enabledFilters + */ + public function __construct(array $enabledFilters) + { + $this->enabledFilters = $enabledFilters; + } + + /** + * Create a connection by name. + * + * @param EntityManager $entityManager + */ + public function configure(EntityManager $entityManager) + { + $this->enableFilters($entityManager); + } + + private function enableFilters(EntityManager $entityManager) + { + if (empty($this->enabledFilters)) { + return; + } + + $filterCollection = $entityManager->getFilters(); + foreach ($this->enabledFilters as $filter) { + $filterCollection->enable($filter); + } + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/ClassMetadataCollection.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/ClassMetadataCollection.php new file mode 100644 index 0000000..e441151 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/ClassMetadataCollection.php @@ -0,0 +1,55 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Mapping; + +/** + * @author Fabien Potencier + */ +class ClassMetadataCollection +{ + private $path; + private $namespace; + private $metadata; + + public function __construct(array $metadata) + { + $this->metadata = $metadata; + } + + public function getMetadata() + { + return $this->metadata; + } + + public function setPath($path) + { + $this->path = $path; + } + + public function getPath() + { + return $this->path; + } + + public function setNamespace($namespace) + { + $this->namespace = $namespace; + } + + public function getNamespace() + { + return $this->namespace; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/DisconnectedMetadataFactory.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/DisconnectedMetadataFactory.php new file mode 100644 index 0000000..ffcb7de --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/DisconnectedMetadataFactory.php @@ -0,0 +1,26 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Mapping; + +/** + * @author Fabien Potencier + */ +class DisconnectedMetadataFactory extends MetadataFactory +{ + protected function getClassMetadataFactoryClass() + { + return 'Doctrine\\ORM\\Tools\\DisconnectedClassMetadataFactory'; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/MetadataFactory.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/MetadataFactory.php new file mode 100644 index 0000000..e6e5199 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Mapping/MetadataFactory.php @@ -0,0 +1,180 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Mapping; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Doctrine\ORM\Mapping\MappingException; + +/** + * This class provides methods to access Doctrine entity class metadata for a + * given bundle, namespace or entity class. + * + * @author Fabien Potencier + */ +class MetadataFactory +{ + private $registry; + + /** + * Constructor. + * + * @param ManagerRegistry $registry A ManagerRegistry instance + */ + public function __construct(ManagerRegistry $registry) + { + $this->registry = $registry; + } + + /** + * Gets the metadata of all classes of a bundle. + * + * @param BundleInterface $bundle A BundleInterface instance + * + * @return ClassMetadataCollection A ClassMetadataCollection instance + */ + public function getBundleMetadata(BundleInterface $bundle) + { + $namespace = $bundle->getNamespace(); + $metadata = $this->getMetadataForNamespace($namespace); + if (!$metadata->getMetadata()) { + throw new \RuntimeException(sprintf('Bundle "%s" does not contain any mapped entities.', $bundle->getName())); + } + + $path = $this->getBasePathForClass($bundle->getName(), $bundle->getNamespace(), $bundle->getPath()); + + $metadata->setPath($path); + $metadata->setNamespace($bundle->getNamespace()); + + return $metadata; + } + + /** + * Gets the metadata of a class. + * + * @param string $class A class name + * @param string $path The path where the class is stored (if known) + * + * @return ClassMetadataCollection A ClassMetadataCollection instance + */ + public function getClassMetadata($class, $path = null) + { + $metadata = $this->getMetadataForClass($class); + if (!$metadata->getMetadata()) { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($class); + } + + $this->findNamespaceAndPathForMetadata($metadata); + + return $metadata; + } + + /** + * Gets the metadata of all classes of a namespace. + * + * @param string $namespace A namespace name + * @param string $path The path where the class is stored (if known) + * + * @return ClassMetadataCollection A ClassMetadataCollection instance + */ + public function getNamespaceMetadata($namespace, $path = null) + { + $metadata = $this->getMetadataForNamespace($namespace); + if (!$metadata->getMetadata()) { + throw new \RuntimeException(sprintf('Namespace "%s" does not contain any mapped entities.', $namespace)); + } + + $this->findNamespaceAndPathForMetadata($metadata, $path); + + return $metadata; + } + + /** + * Find and configure path and namespace for the metadata collection. + * + * @param ClassMetadataCollection $metadata + * @param string|null $path + * @return void + */ + public function findNamespaceAndPathForMetadata(ClassMetadataCollection $metadata, $path = null) + { + $all = $metadata->getMetadata(); + if (class_exists($all[0]->name)) { + $r = new \ReflectionClass($all[0]->name); + $path = $this->getBasePathForClass($r->getName(), $r->getNamespaceName(), dirname($r->getFilename())); + } elseif (!$path) { + throw new \RuntimeException(sprintf('Unable to determine where to save the "%s" class (use the --path option).', $all[0]->name)); + } + + $metadata->setPath($path); + $metadata->setNamespace(isset($r) ? $r->getNamespaceName() : $all[0]->name); + } + + private function getBasePathForClass($name, $namespace, $path) + { + $namespace = str_replace('\\', '/', $namespace); + $search = str_replace('\\', '/', $path); + $destination = str_replace('/'.$namespace, '', $search, $c); + + if ($c != 1) { + throw new \RuntimeException(sprintf('Can\'t find base path for "%s" (path: "%s", destination: "%s").', $name, $path, $destination)); + } + + return $destination; + } + + private function getMetadataForNamespace($namespace) + { + $metadata = array(); + foreach ($this->getAllMetadata() as $m) { + if (strpos($m->name, $namespace) === 0) { + $metadata[] = $m; + } + } + + return new ClassMetadataCollection($metadata); + } + + private function getMetadataForClass($entity) + { + foreach ($this->getAllMetadata() as $metadata) { + if ($metadata->name === $entity) { + return new ClassMetadataCollection(array($metadata)); + } + } + + return new ClassMetadataCollection(array()); + } + + private function getAllMetadata() + { + $metadata = array(); + foreach ($this->registry->getManagers() as $em) { + $class = $this->getClassMetadataFactoryClass(); + $cmf = new $class(); + $cmf->setEntityManager($em); + foreach ($cmf->getAllMetadata() as $m) { + $metadata[] = $m; + } + } + + return $metadata; + } + + protected function getClassMetadataFactoryClass() + { + return 'Doctrine\\ORM\\Mapping\\ClassMetadataFactory'; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/README.md b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/README.md new file mode 100644 index 0000000..31391c5 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/README.md @@ -0,0 +1,56 @@ +# Doctrine Bundle + +Doctrine DBAL & ORM Bundle for the Symfony Framework. + +Because Symfony 2 does not want to force or suggest a specific persistence solutions on the users +this bundle was removed from the core of the Symfony 2 framework. Doctrine2 will still be a major player +in the Symfony world and the bundle is maintained by developers in the Doctrine and Symfony communities. + + IMPORTANT: This bundle is developed for Symfony 2.1 and up. For Symfony 2.0 applications the DoctrineBundle + is still shipped with the core Symfony repository. + +Build Status: [![Build Status](https://secure.travis-ci.org/doctrine/DoctrineBundle.png?branch=master)](http://travis-ci.org/doctrine/DoctrineBundle) + +## What is Doctrine? + +The Doctrine Project is the home of a selected set of PHP libraries primarily focused on providing persistence +services and related functionality. Its prize projects are a Object Relational Mapper and the Database Abstraction +Layer it is built on top of. You can read more about the projects below or view a list of all projects. + +Object relational mapper (ORM) for PHP that sits on top of a powerful database abstraction layer (DBAL). +One of its key features is the option to write database queries in a proprietary object oriented SQL dialect +called Doctrine Query Language (DQL), inspired by Hibernates HQL. This provides developers with a powerful +alternative to SQL that maintains flexibility without requiring unnecessary code duplication. + +DBAL is a powerful database abstraction layer with many features for database schema introspection, +schema management and PDO abstraction. + +## Installation + +### 1. Old deps and bin/vendors way + +Add the following snippets to "deps" files: + + [doctrine-dbal] + git=http://github.com/doctrine/dbal.git + + [doctrine-orm] + git=http://github.com/doctrine/doctrine2.git + + [DoctrineBundle] + git=http://github.com/doctrine/DoctrineBundle.git + target=/bundles/Doctrine/Bundle/DoctrineBundle + +### 2. Composer + +Add the following dependencies to your projects composer.json file: + + "require": { + # .. + "doctrine/doctrine-bundle": ">=2.1" + # .. + } + +## Documentation + +See the Resources/docs folder more a full documentation. diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Registry.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Registry.php new file mode 100644 index 0000000..1b10b7f --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Registry.php @@ -0,0 +1,155 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Bridge\Doctrine\RegistryInterface; +use Symfony\Bridge\Doctrine\ManagerRegistry; +use Doctrine\ORM\ORMException; +use Doctrine\ORM\EntityManager; + +/** + * References all Doctrine connections and entity managers in a given Container. + * + * @author Fabien Potencier + */ +class Registry extends ManagerRegistry implements RegistryInterface +{ + public function __construct(ContainerInterface $container, array $connections, array $entityManagers, $defaultConnection, $defaultEntityManager) + { + $this->setContainer($container); + + parent::__construct('ORM', $connections, $entityManagers, $defaultConnection, $defaultEntityManager, 'Doctrine\ORM\Proxy\Proxy'); + } + + /** + * Gets the default entity manager name. + * + * @return string The default entity manager name + * + * @deprecated + */ + public function getDefaultEntityManagerName() + { + return $this->getDefaultManagerName(); + } + + /** + * Gets a named entity manager. + * + * @param string $name The entity manager name (null for the default one) + * + * @return EntityManager + * + * @deprecated + */ + public function getEntityManager($name = null) + { + return $this->getManager($name); + } + + /** + * Gets an array of all registered entity managers + * + * @return EntityManager[] an array of all EntityManager instances + * + * @deprecated + */ + public function getEntityManagers() + { + return $this->getManagers(); + } + + /** + * Resets a named entity manager. + * + * This method is useful when an entity manager has been closed + * because of a rollbacked transaction AND when you think that + * it makes sense to get a new one to replace the closed one. + * + * Be warned that you will get a brand new entity manager as + * the existing one is not useable anymore. This means that any + * other object with a dependency on this entity manager will + * hold an obsolete reference. You can inject the registry instead + * to avoid this problem. + * + * @param string $name The entity manager name (null for the default one) + * + * @return EntityManager + */ + public function resetEntityManager($name = null) + { + $this->resetManager($name); + } + + /** + * Resolves a registered namespace alias to the full namespace. + * + * This method looks for the alias in all registered entity managers. + * + * @param string $alias The alias + * + * @return string The full namespace + */ + public function getEntityNamespace($alias) + { + return $this->getAliasNamespace($alias); + } + + /** + * Resolves a registered namespace alias to the full namespace. + * + * This method looks for the alias in all registered entity managers. + * + * @param string $alias The alias + * + * @return string The full namespace + * + * @see Configuration::getEntityNamespace + */ + public function getAliasNamespace($alias) + { + foreach (array_keys($this->getManagers()) as $name) { + try { + return $this->getManager($name)->getConfiguration()->getEntityNamespace($alias); + } catch (ORMException $e) { + } + } + + throw ORMException::unknownEntityNamespace($alias); + } + + /** + * Gets all connection names. + * + * @return array An array of connection names + */ + public function getEntityManagerNames() + { + return $this->getManagerNames(); + } + + /** + * Gets the entity manager associated with a given class. + * + * @param string $class A Doctrine Entity class name + * + * @return EntityManager|null + */ + public function getEntityManagerForClass($class) + { + return $this->getManagerForClass($class); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/dbal.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/dbal.xml new file mode 100644 index 0000000..ff85087 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/dbal.xml @@ -0,0 +1,62 @@ + + + + + + Doctrine\DBAL\Logging\LoggerChain + Doctrine\DBAL\Logging\DebugStack + Symfony\Bridge\Doctrine\Logger\DbalLogger + Doctrine\DBAL\Configuration + Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector + Symfony\Bridge\Doctrine\ContainerAwareEventManager + Doctrine\Bundle\DoctrineBundle\ConnectionFactory + Doctrine\DBAL\Event\Listeners\MysqlSessionInit + Doctrine\DBAL\Event\Listeners\OracleSessionInit + Doctrine\Bundle\DoctrineBundle\Registry + + + + + + + + + + + + + + + + + + + + + + + + + + %doctrine.dbal.connection_factory.types% + + + + + + + + + + + + + %doctrine.connections% + %doctrine.entity_managers% + %doctrine.default_connection% + %doctrine.default_entity_manager% + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/orm.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/orm.xml new file mode 100644 index 0000000..211fe7a --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/orm.xml @@ -0,0 +1,97 @@ + + + + + + Doctrine\ORM\Configuration + Doctrine\ORM\EntityManager + Doctrine\Bundle\DoctrineBundle\ManagerConfigurator + + + Doctrine\Common\Cache\ArrayCache + Doctrine\Common\Cache\ApcCache + Doctrine\Common\Cache\MemcacheCache + localhost + 11211 + Memcache + Doctrine\Common\Cache\MemcachedCache + localhost + 11211 + Memcached + Doctrine\Common\Cache\XcacheCache + + + Doctrine\ORM\Mapping\Driver\DriverChain + Doctrine\ORM\Mapping\Driver\AnnotationDriver + Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver + Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver + Doctrine\ORM\Mapping\Driver\PHPDriver + Doctrine\ORM\Mapping\Driver\StaticPHPDriver + + + Symfony\Bridge\Doctrine\CacheWarmer\ProxyCacheWarmer + + + Symfony\Bridge\Doctrine\Form\DoctrineOrmTypeGuesser + + + Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator + Symfony\Bridge\Doctrine\Validator\DoctrineInitializer + + + Symfony\Bridge\Doctrine\Security\User\EntityUserProvider + + + Doctrine\ORM\Tools\ResolveTargetEntityListener + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/schema/doctrine-1.0.xsd b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/schema/doctrine-1.0.xsd new file mode 100644 index 0000000..76170b9 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/config/schema/doctrine-1.0.xsd @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/doc/configuration.rst b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/doc/configuration.rst new file mode 100644 index 0000000..088eb49 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/doc/configuration.rst @@ -0,0 +1,305 @@ +.. index:: + single: Doctrine; ORM Configuration Reference + single: Configuration Reference; Doctrine ORM + +Configuration Reference +======================= + +.. configuration-block:: + + .. code-block:: yaml + + doctrine: + dbal: + default_connection: default + connections: + default: + dbname: database + host: localhost + port: 1234 + user: user + password: secret + driver: pdo_mysql + driver_class: MyNamespace\MyDriverImpl + options: + foo: bar + path: %kernel.data_dir%/data.sqlite + memory: true + unix_socket: /tmp/mysql.sock + wrapper_class: MyDoctrineDbalConnectionWrapper + charset: UTF8 + logging: %kernel.debug% + platform_service: MyOwnDatabasePlatformService + mapping_types: + enum: string + conn1: + # ... + types: + custom: Acme\HelloBundle\MyCustomType + orm: + auto_generate_proxy_classes: false + proxy_namespace: Proxies + proxy_dir: %kernel.cache_dir%/doctrine/orm/Proxies + default_entity_manager: default # The first defined is used if not set + entity_managers: + default: + # The name of a DBAL connection (the one marked as default is used if not set) + connection: conn1 + mappings: # Required + AcmeHelloBundle: ~ + class_metadata_factory_name: Doctrine\ORM\Mapping\ClassMetadataFactory + # All cache drivers have to be array, apc, xcache or memcache + metadata_cache_driver: array + query_cache_driver: array + result_cache_driver: + type: memcache + host: localhost + port: 11211 + instance_class: Memcache + class: Doctrine\Common\Cache\MemcacheCache + dql: + string_functions: + test_string: Acme\HelloBundle\DQL\StringFunction + numeric_functions: + test_numeric: Acme\HelloBundle\DQL\NumericFunction + datetime_functions: + test_datetime: Acme\HelloBundle\DQL\DatetimeFunction + em2: + # ... + + .. code-block:: xml + + + + + + + bar + string + + + Acme\HelloBundle\MyCustomType + + + + + + + + Acme\HelloBundle\DQL\NumericFunction + + + + + + + +Configuration Overview +---------------------- + +This following configuration example shows all the configuration defaults that +the ORM resolves to: + +.. code-block:: yaml + + doctrine: + orm: + auto_mapping: true + # the standard distribution overrides this to be true in debug, false otherwise + auto_generate_proxy_classes: false + proxy_namespace: Proxies + proxy_dir: %kernel.cache_dir%/doctrine/orm/Proxies + default_entity_manager: default + metadata_cache_driver: array + query_cache_driver: array + result_cache_driver: array + +There are lots of other configuration options that you can use to overwrite +certain classes, but those are for very advanced use-cases only. + +Caching Drivers +~~~~~~~~~~~~~~~ + +For the caching drivers you can specify the values "array", "apc", "memcache" +or "xcache". + +The following example shows an overview of the caching configurations: + +.. code-block:: yaml + + doctrine: + orm: + auto_mapping: true + metadata_cache_driver: apc + query_cache_driver: xcache + result_cache_driver: + type: memcache + host: localhost + port: 11211 + instance_class: Memcache + +Mapping Configuration +~~~~~~~~~~~~~~~~~~~~~ + +Explicit definition of all the mapped entities is the only necessary +configuration for the ORM and there are several configuration options that you +can control. The following configuration options exist for a mapping: + +* ``type`` One of ``annotation``, ``xml``, ``yml``, ``php`` or ``staticphp``. + This specifies which type of metadata type your mapping uses. + +* ``dir`` Path to the mapping or entity files (depending on the driver). If + this path is relative it is assumed to be relative to the bundle root. This + only works if the name of your mapping is a bundle name. If you want to use + this option to specify absolute paths you should prefix the path with the + kernel parameters that exist in the DIC (for example %kernel.root_dir%). + +* ``prefix`` A common namespace prefix that all entities of this mapping + share. This prefix should never conflict with prefixes of other defined + mappings otherwise some of your entities cannot be found by Doctrine. This + option defaults to the bundle namespace + ``Entity``, for example for an + application bundle called ``AcmeHelloBundle`` prefix would be + ``Acme\HelloBundle\Entity``. + +* ``alias`` Doctrine offers a way to alias entity namespaces to simpler, + shorter names to be used in DQL queries or for Repository access. When using + a bundle the alias defaults to the bundle name. + +* ``is_bundle`` This option is a derived value from ``dir`` and by default is + set to true if dir is relative proved by a ``file_exists()`` check that + returns false. It is false if the existence check returns true. In this case + an absolute path was specified and the metadata files are most likely in a + directory outside of a bundle. + +.. index:: + single: Configuration; Doctrine DBAL + single: Doctrine; DBAL configuration + +.. _`reference-dbal-configuration`: + +Doctrine DBAL Configuration +--------------------------- + +.. note:: + + DoctrineBundle supports all parameters that default Doctrine drivers + accept, converted to the XML or YAML naming standards that Symfony + enforces. See the Doctrine `DBAL documentation`_ for more information. + +Besides default Doctrine options, there are some Symfony-related ones that you +can configure. The following block shows all possible configuration keys: + +.. configuration-block:: + + .. code-block:: yaml + + doctrine: + dbal: + dbname: database + host: localhost + port: 1234 + user: user + password: secret + driver: pdo_mysql + driver_class: MyNamespace\MyDriverImpl + options: + foo: bar + path: %kernel.data_dir%/data.sqlite + memory: true + unix_socket: /tmp/mysql.sock + wrapper_class: MyDoctrineDbalConnectionWrapper + charset: UTF8 + logging: %kernel.debug% + platform_service: MyOwnDatabasePlatformService + mapping_types: + enum: string + types: + custom: Acme\HelloBundle\MyCustomType + + .. code-block:: xml + + + + + + + bar + string + Acme\HelloBundle\MyCustomType + + + +If you want to configure multiple connections in YAML, put them under the +``connections`` key and give them a unique name: + +.. code-block:: yaml + + doctrine: + dbal: + default_connection: default + connections: + default: + dbname: Symfony2 + user: root + password: null + host: localhost + customer: + dbname: customer + user: root + password: null + host: localhost + +The ``database_connection`` service always refers to the *default* connection, +which is the first one defined or the one configured via the +``default_connection`` parameter. + +Each connection is also accessible via the ``doctrine.dbal.[name]_connection`` +service where ``[name]`` if the name of the connection. + +.. _DBAL documentation: http://www.doctrine-project.org/docs/dbal/2.0/en diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/doc/installation.rst b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/doc/installation.rst new file mode 100644 index 0000000..1ee5a31 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/doc/installation.rst @@ -0,0 +1,43 @@ +Doctrine Bundle +=============== + +Doctrine DBAL & ORM Bundle for the Symfony Framework. + +Because Symfony 2 does not want to force or suggest a specific persistence solutions on the users +this bundle was removed from the core of the Symfony 2 framework. Doctrine2 will still be a major player +in the Symfony world and the bundle is maintained by developers in the Doctrine and Symfony communities. + +IMPORTANT: This bundle is developed for Symfony 2.1 and up. For Symfony 2.0 applications the DoctrineBundle +is still shipped with the core Symfony repository. + +Installation +------------ + +1. Old deps and bin/vendors way + +Add the following snippets to "deps" files: + +.. code-block:: + + [doctrine-mongodb] + git=http://github.com/doctrine/dbal.git + + [doctrine-mongodb-odm] + git=http://github.com/doctrine/doctrine2.git + + [DoctrineBundle] + git=http://github.com/doctrine/DoctrineBundle.git + target=/bundles/Doctrine/Bundle/DoctrineBundle + +2. Composer + +Add the following dependencies to your projects composer.json file: + +.. code-block:: + + "require": { + # .. + "doctrine/doctrine-bundle": ">=2.1" + # .. + } + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/views/Collector/db.html.twig b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/views/Collector/db.html.twig new file mode 100644 index 0000000..fa2aed3 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/views/Collector/db.html.twig @@ -0,0 +1,122 @@ +{% extends app.request.isXmlHttpRequest ? 'WebProfilerBundle:Profiler:ajax_layout.html.twig' : 'WebProfilerBundle:Profiler:layout.html.twig' %} + +{% block toolbar %} + {% set icon %} + Database + {{ collector.querycount }} + in {{ '%0.2f'|format(collector.time * 1000) }} ms + {% endset %} + {% set text %} +
    + DB Queries + {{ collector.querycount }} +
    +
    + Query time + {{ '%0.2f'|format(collector.time * 1000) }} ms +
    + {% endset %} + {% include 'WebProfilerBundle:Profiler:toolbar_item.html.twig' with { 'link': profiler_url } %} +{% endblock %} + +{% block menu %} + + + Doctrine + + {{ collector.querycount }} + {{ '%0.0f'|format(collector.time * 1000) }} ms + + +{% endblock %} + +{% block panel %} + {% if 'explain' == page %} + {% render 'DoctrineBundle:Profiler:explain' with { + 'token': token, + 'panel': 'db', + 'connectionName': app.request.query.get('connection'), + 'query': app.request.query.get('query') + } %} + {% else %} + {{ block('queries') }} + {% endif %} +{% endblock %} + +{% block queries %} +

    Queries

    + + {% for connection, queries in collector.queries %} +

    Connection {{ connection }}

    + {% if queries is empty %} +

    + No queries. +

    + {% else %} +
      + {% for i, query in queries %} +
    • +
      + {% if query.explainable %} + + + + - + + {% endif %} + {{ query.sql }} +
      + + Parameters: {{ query.params|yaml_encode }}
      + Time: {{ '%0.2f'|format(query.executionMS * 1000) }} ms +
      + {% if query.explainable %} +
      + {% endif %} +
    • + {% endfor %} +
    + {% endif %} + {% endfor %} + +

    Database Connections

    + + {% if collector.connections %} + {% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.connections} only %} + {% else %} +

    + No connections. +

    + {% endif %} + +

    Entity Managers

    + + {% if collector.managers %} + {% include 'WebProfilerBundle:Profiler:table.html.twig' with {data: collector.managers} only %} + {% else %} +

    + No entity managers. +

    + {% endif %} + + +{% endblock %} \ No newline at end of file diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/views/Collector/explain.html.twig b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/views/Collector/explain.html.twig new file mode 100644 index 0000000..22af37f --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Resources/views/Collector/explain.html.twig @@ -0,0 +1,20 @@ +Explanation: + + + + + {% for label in data[0]|keys %} + + {% endfor %} + + + + {% for row in data %} + + {% for item in row %} + + {% endfor %} + + {% endfor %} + +
    {{ label }}
    {{ item }}
    \ No newline at end of file diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/BundleTest.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/BundleTest.php new file mode 100644 index 0000000..94eaccd --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/BundleTest.php @@ -0,0 +1,47 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Tests; + +use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\DoctrineValidationPass; +use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; + +class BundleTest extends \PHPUnit_Framework_TestCase +{ + public function testBuildCompilerPasses() + { + $container = new ContainerBuilder(); + $bundle = new DoctrineBundle(); + $bundle->build($container); + + $config = $container->getCompilerPassConfig(); + $passes = $config->getBeforeOptimizationPasses(); + + $foundEventListener = false; + $foundValidation = false; + + foreach ($passes as $pass) { + if ($pass instanceof RegisterEventListenersAndSubscribersPass) { + $foundEventListener = true; + } elseif ($pass instanceof DoctrineValidationPass) { + $foundValidation = true; + } + } + + $this->assertTrue($foundEventListener, 'RegisterEventListenersAndSubcribersPass was not found'); + $this->assertTrue($foundValidation, 'DoctrineValidationPass was not found'); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/ContainerTest.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/ContainerTest.php new file mode 100644 index 0000000..83de54c --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/ContainerTest.php @@ -0,0 +1,59 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Tests; + +use Doctrine\DBAL\Types\Type; + +class ContainerTest extends TestCase +{ + protected function setUp() + { + parent::setUp(); + + if (!class_exists('Doctrine\\ORM\\Version')) { + $this->markTestSkipped('Doctrine ORM is not available.'); + } + } + + public function testContainer() + { + $container = $this->createYamlBundleTestContainer(); + + $this->assertInstanceOf('Symfony\Bridge\Doctrine\Logger\DbalLogger', $container->get('doctrine.dbal.logger')); + $this->assertInstanceOf('Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector', $container->get('data_collector.doctrine')); + $this->assertInstanceOf('Doctrine\DBAL\Configuration', $container->get('doctrine.dbal.default_connection.configuration')); + $this->assertInstanceOf('Doctrine\Common\EventManager', $container->get('doctrine.dbal.default_connection.event_manager')); + $this->assertInstanceOf('Doctrine\DBAL\Connection', $container->get('doctrine.dbal.default_connection')); + $this->assertInstanceOf('Doctrine\Common\Annotations\Reader', $container->get('doctrine.orm.metadata.annotation_reader')); + $this->assertInstanceOf('Doctrine\ORM\Configuration', $container->get('doctrine.orm.default_configuration')); + $this->assertInstanceOf('Doctrine\ORM\Mapping\Driver\DriverChain', $container->get('doctrine.orm.default_metadata_driver')); + $this->assertInstanceOf('Doctrine\Common\Cache\ArrayCache', $container->get('doctrine.orm.default_metadata_cache')); + $this->assertInstanceOf('Doctrine\Common\Cache\ArrayCache', $container->get('doctrine.orm.default_query_cache')); + $this->assertInstanceOf('Doctrine\Common\Cache\ArrayCache', $container->get('doctrine.orm.default_result_cache')); + $this->assertInstanceOf('Doctrine\ORM\EntityManager', $container->get('doctrine.orm.default_entity_manager')); + $this->assertInstanceOf('Doctrine\DBAL\Connection', $container->get('database_connection')); + $this->assertInstanceOf('Doctrine\ORM\EntityManager', $container->get('doctrine.orm.entity_manager')); + $this->assertInstanceOf('Doctrine\Common\EventManager', $container->get('doctrine.orm.default_entity_manager.event_manager')); + $this->assertInstanceOf('Doctrine\Common\EventManager', $container->get('doctrine.dbal.event_manager')); + $this->assertInstanceOf('Doctrine\DBAL\Event\Listeners\MysqlSessionInit', $container->get('doctrine.dbal.default_connection.events.mysqlsessioninit')); + $this->assertInstanceOf('Symfony\Bridge\Doctrine\CacheWarmer\ProxyCacheWarmer', $container->get('doctrine.orm.proxy_cache_warmer')); + $this->assertInstanceOf('Doctrine\Common\Persistence\ManagerRegistry', $container->get('doctrine')); + $this->assertInstanceOf('Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator', $container->get('doctrine.orm.validator.unique')); + + $this->assertSame($container->get('my.platform'), $container->get('doctrine.dbal.default_connection')->getDatabasePlatform()); + + $this->assertTrue(Type::hasType('test')); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php new file mode 100644 index 0000000..b200fc3 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php @@ -0,0 +1,854 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection; + +use Doctrine\Bundle\DoctrineBundle\Tests\TestCase; +use Doctrine\Bundle\DoctrineBundle\DependencyInjection\DoctrineExtension; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass; +use Symfony\Component\Config\FileLocator; + +abstract class AbstractDoctrineExtensionTest extends \PHPUnit_Framework_TestCase +{ + abstract protected function loadFromFile(ContainerBuilder $container, $file); + + public function testDbalOverrideDefaultConnection() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + + $loader->load(array(array(), array('dbal' => array('default_connection' => 'foo')), array()), $container); + + // doctrine.dbal.default_connection + $this->assertEquals('%doctrine.default_connection%', $container->getDefinition('doctrine')->getArgument(3), '->load() overrides existing configuration options'); + $this->assertEquals('foo', $container->getParameter('doctrine.default_connection'), '->load() overrides existing configuration options'); + + } + + public function testDbalLoad() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + + $loader->load(array(array('dbal' => array('connections' => array('default' => array('password' => 'foo')))), array(), array('dbal' => array('default_connection' => 'foo')), array()), $container); + + $config = $container->getDefinition('doctrine.dbal.default_connection')->getArgument(0); + + $this->assertEquals('foo', $config['password']); + $this->assertEquals('root', $config['user']); + } + + public function testDbalLoadFromXmlMultipleConnections() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + + $this->loadFromFile($container, 'dbal_service_multiple_connections'); + + $this->compileContainer($container); + + // doctrine.dbal.mysql_connection + $config = $container->getDefinition('doctrine.dbal.mysql_connection')->getArgument(0); + + $this->assertEquals('mysql_s3cr3t', $config['password']); + $this->assertEquals('mysql_user', $config['user']); + $this->assertEquals('mysql_db', $config['dbname']); + $this->assertEquals('/path/to/mysqld.sock', $config['unix_socket']); + + // doctrine.dbal.sqlite_connection + $config = $container->getDefinition('doctrine.dbal.sqlite_connection')->getArgument(0); + $this->assertArrayHasKey('memory', $config); + + // doctrine.dbal.oci8_connection + $config = $container->getDefinition('doctrine.dbal.oci_connection')->getArgument(0); + $this->assertArrayHasKey('charset', $config); + } + + public function testDbalLoadFromXmlSingleConnections() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + + $this->loadFromFile($container, 'dbal_service_single_connection'); + + $this->compileContainer($container); + + // doctrine.dbal.mysql_connection + $config = $container->getDefinition('doctrine.dbal.default_connection')->getArgument(0); + + $this->assertEquals('mysql_s3cr3t', $config['password']); + $this->assertEquals('mysql_user', $config['user']); + $this->assertEquals('mysql_db', $config['dbname']); + $this->assertEquals('/path/to/mysqld.sock', $config['unix_socket']); + } + + public function testDbalLoadSingleMasterSlaveConnection() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + + $this->loadFromFile($container, 'dbal_service_single_master_slave_connection'); + + $this->compileContainer($container); + + // doctrine.dbal.mysql_connection + $param = $container->getDefinition('doctrine.dbal.default_connection')->getArgument(0); + + $this->assertEquals('Doctrine\\DBAL\\Connections\\MasterSlaveConnection', $param['wrapperClass']); + $this->assertEquals( + array('user' => 'mysql_user', 'password' => 'mysql_s3cr3t', 'port' => null, 'dbname' => 'mysql_db', 'host' => 'localhost', 'unix_socket' => '/path/to/mysqld.sock'), + $param['master'] + ); + $this->assertEquals( + array( + 'user' => 'slave_user', 'password' => 'slave_s3cr3t', 'port' => null, 'dbname' => 'slave_db', + 'host' => 'localhost', 'unix_socket' => '/path/to/mysqld_slave.sock' + ), + $param['slaves']['slave1'] + ); + } + + public function testDependencyInjectionConfigurationDefaults() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + + $loader->load(array(array('dbal' => array('connections' => array('default' => array('password' => 'foo'))), 'orm' => array('default_entity_manager' => 'default', 'entity_managers' => array('default' => array('mappings' => array('YamlBundle' => array())))))), $container); + + $this->assertFalse($container->getParameter('doctrine.orm.auto_generate_proxy_classes')); + $this->assertEquals('Doctrine\ORM\Configuration', $container->getParameter('doctrine.orm.configuration.class')); + $this->assertEquals('Doctrine\ORM\EntityManager', $container->getParameter('doctrine.orm.entity_manager.class')); + $this->assertEquals('Proxies', $container->getParameter('doctrine.orm.proxy_namespace')); + $this->assertEquals('Doctrine\Common\Cache\ArrayCache', $container->getParameter('doctrine.orm.cache.array.class')); + $this->assertEquals('Doctrine\Common\Cache\ApcCache', $container->getParameter('doctrine.orm.cache.apc.class')); + $this->assertEquals('Doctrine\Common\Cache\MemcacheCache', $container->getParameter('doctrine.orm.cache.memcache.class')); + $this->assertEquals('localhost', $container->getParameter('doctrine.orm.cache.memcache_host')); + $this->assertEquals('11211', $container->getParameter('doctrine.orm.cache.memcache_port')); + $this->assertEquals('Memcache', $container->getParameter('doctrine.orm.cache.memcache_instance.class')); + $this->assertEquals('Doctrine\Common\Cache\XcacheCache', $container->getParameter('doctrine.orm.cache.xcache.class')); + $this->assertEquals('Doctrine\ORM\Mapping\Driver\DriverChain', $container->getParameter('doctrine.orm.metadata.driver_chain.class')); + $this->assertEquals('Doctrine\ORM\Mapping\Driver\AnnotationDriver', $container->getParameter('doctrine.orm.metadata.annotation.class')); + $this->assertEquals('Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver', $container->getParameter('doctrine.orm.metadata.xml.class')); + $this->assertEquals('Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver', $container->getParameter('doctrine.orm.metadata.yml.class')); + + $config = array( + 'proxy_namespace' => 'MyProxies', + 'auto_generate_proxy_classes' => true, + 'default_entity_manager' => 'default', + 'entity_managers' => array( + 'default' => array( + 'mappings' => array('YamlBundle' => array()), + ) + ) + ); + + $container = $this->getContainer(); + $loader->load(array(array('dbal' => array('connections' => array('default' => array('password' => 'foo'))), 'orm' => $config)), $container); + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.dbal.default_connection'); + + $args = $definition->getArguments(); + $this->assertEquals('pdo_mysql', $args[0]['driver']); + $this->assertEquals('localhost', $args[0]['host']); + $this->assertEquals('root', $args[0]['user']); + $this->assertEquals('doctrine.dbal.default_connection.configuration', (string) $args[1]); + $this->assertEquals('doctrine.dbal.default_connection.event_manager', (string) $args[2]); + + $definition = $container->getDefinition('doctrine.orm.default_entity_manager'); + $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getClass()); + $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getFactoryClass()); + $this->assertEquals('create', $definition->getFactoryMethod()); + + $this->assertEquals(array('default' => 'doctrine.orm.default_entity_manager'), $container->getParameter('doctrine.entity_managers'), "Set of the existing EntityManagers names is incorrect."); + $this->assertEquals('%doctrine.entity_managers%', $container->getDefinition('doctrine')->getArgument(2), "Set of the existing EntityManagers names is incorrect."); + + $arguments = $definition->getArguments(); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Reference', $arguments[0]); + $this->assertEquals('doctrine.dbal.default_connection', (string) $arguments[0]); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Reference', $arguments[1]); + $this->assertEquals('doctrine.orm.default_configuration', (string) $arguments[1]); + + $definition = $container->getDefinition('doctrine.orm.default_configuration'); + $calls = array_values($definition->getMethodCalls()); + $this->assertEquals(array('YamlBundle' => 'Fixtures\Bundles\YamlBundle\Entity'), $calls[0][1][0]); + $this->assertEquals('doctrine.orm.default_metadata_cache', (string) $calls[1][1][0]); + $this->assertEquals('doctrine.orm.default_query_cache', (string) $calls[2][1][0]); + $this->assertEquals('doctrine.orm.default_result_cache', (string) $calls[3][1][0]); + + $definition = $container->getDefinition('doctrine.orm.default_metadata_cache'); + $this->assertEquals('%doctrine.orm.cache.array.class%', $definition->getClass()); + + $definition = $container->getDefinition('doctrine.orm.default_query_cache'); + $this->assertEquals('%doctrine.orm.cache.array.class%', $definition->getClass()); + + $definition = $container->getDefinition('doctrine.orm.default_result_cache'); + $this->assertEquals('%doctrine.orm.cache.array.class%', $definition->getClass()); + } + + public function testSingleEntityManagerConfiguration() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + + $loader->load(array(array('dbal' => array('connections' => array('default' => array('password' => 'foo'))), 'orm' => array('default_entity_manager' => 'default', 'entity_managers' => array('default' => array('mappings' => array('YamlBundle' => array())))))), $container); + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.dbal.default_connection'); + + $definition = $container->getDefinition('doctrine.orm.default_entity_manager'); + $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getClass()); + $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getFactoryClass()); + $this->assertEquals('create', $definition->getFactoryMethod()); + + $this->assertDICConstructorArguments($definition, array( + new Reference('doctrine.dbal.default_connection'), new Reference('doctrine.orm.default_configuration') + )); + } + + public function testLoadSimpleSingleConnection() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + + $this->loadFromFile($container, 'orm_service_simple_single_entity_manager'); + + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.dbal.default_connection'); + + $this->assertDICConstructorArguments($definition, array( + array( + 'dbname' => 'db', + 'host' => 'localhost', + 'port' => null, + 'user' => 'root', + 'password' => null, + 'driver' => 'pdo_mysql', + 'driverOptions' => array(), + ), + new Reference('doctrine.dbal.default_connection.configuration'), + new Reference('doctrine.dbal.default_connection.event_manager'), + array(), + )); + + $definition = $container->getDefinition('doctrine.orm.default_entity_manager'); + $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getClass()); + $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getFactoryClass()); + $this->assertEquals('create', $definition->getFactoryMethod()); + + $this->assertDICConstructorArguments($definition, array( + new Reference('doctrine.dbal.default_connection'), new Reference('doctrine.orm.default_configuration') + )); + } + + public function testLoadSingleConnection() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + + $this->loadFromFile($container, 'orm_service_single_entity_manager'); + + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.dbal.default_connection'); + + $this->assertDICConstructorArguments($definition, array( + array( + 'host' => 'localhost', + 'driver' => 'pdo_sqlite', + 'driverOptions' => array(), + 'user' => 'sqlite_user', + 'port' => null, + 'password' => 'sqlite_s3cr3t', + 'dbname' => 'sqlite_db', + 'memory' => true, + ), + new Reference('doctrine.dbal.default_connection.configuration'), + new Reference('doctrine.dbal.default_connection.event_manager'), + array(), + )); + + $definition = $container->getDefinition('doctrine.orm.default_entity_manager'); + $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getClass()); + $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getFactoryClass()); + $this->assertEquals('create', $definition->getFactoryMethod()); + + $this->assertDICConstructorArguments($definition, array( + new Reference('doctrine.dbal.default_connection'), new Reference('doctrine.orm.default_configuration') + )); + + $configDef = $container->getDefinition('doctrine.orm.default_configuration'); + $this->assertDICDefinitionMethodCallOnce($configDef, 'setDefaultRepositoryClassName', array('Acme\Doctrine\Repository')); + } + + public function testLoadMultipleConnections() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + + $this->loadFromFile($container, 'orm_service_multiple_entity_managers'); + + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.dbal.conn1_connection'); + + $args = $definition->getArguments(); + $this->assertEquals('pdo_sqlite', $args[0]['driver']); + $this->assertEquals('localhost', $args[0]['host']); + $this->assertEquals('sqlite_user', $args[0]['user']); + $this->assertEquals('doctrine.dbal.conn1_connection.configuration', (string) $args[1]); + $this->assertEquals('doctrine.dbal.conn1_connection.event_manager', (string) $args[2]); + + $this->assertEquals('doctrine.orm.em2_entity_manager', (string) $container->getAlias('doctrine.orm.entity_manager')); + + $definition = $container->getDefinition('doctrine.orm.em1_entity_manager'); + $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getClass()); + $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getFactoryClass()); + $this->assertEquals('create', $definition->getFactoryMethod()); + + $arguments = $definition->getArguments(); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Reference', $arguments[0]); + $this->assertEquals('doctrine.dbal.conn1_connection', (string) $arguments[0]); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Reference', $arguments[1]); + $this->assertEquals('doctrine.orm.em1_configuration', (string) $arguments[1]); + + $definition = $container->getDefinition('doctrine.dbal.conn2_connection'); + + $args = $definition->getArguments(); + $this->assertEquals('pdo_sqlite', $args[0]['driver']); + $this->assertEquals('localhost', $args[0]['host']); + $this->assertEquals('sqlite_user', $args[0]['user']); + $this->assertEquals('doctrine.dbal.conn2_connection.configuration', (string) $args[1]); + $this->assertEquals('doctrine.dbal.conn2_connection.event_manager', (string) $args[2]); + + $definition = $container->getDefinition('doctrine.orm.em2_entity_manager'); + $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getClass()); + $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getFactoryClass()); + $this->assertEquals('create', $definition->getFactoryMethod()); + + $arguments = $definition->getArguments(); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Reference', $arguments[0]); + $this->assertEquals('doctrine.dbal.conn2_connection', (string) $arguments[0]); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Reference', $arguments[1]); + $this->assertEquals('doctrine.orm.em2_configuration', (string) $arguments[1]); + + $definition = $container->getDefinition('doctrine.orm.em1_metadata_cache'); + $this->assertEquals('%doctrine.orm.cache.xcache.class%', $definition->getClass()); + + $definition = $container->getDefinition('doctrine.orm.em1_query_cache'); + $this->assertEquals('%doctrine.orm.cache.array.class%', $definition->getClass()); + + $definition = $container->getDefinition('doctrine.orm.em1_result_cache'); + $this->assertEquals('%doctrine.orm.cache.array.class%', $definition->getClass()); + } + + public function testLoadLogging() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + + $this->loadFromFile($container, 'dbal_logging'); + + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.dbal.log_connection.configuration'); + $this->assertDICDefinitionMethodCallOnce($definition, 'setSQLLogger', array(new Reference('doctrine.dbal.logger'))); + + $definition = $container->getDefinition('doctrine.dbal.profile_connection.configuration'); + $this->assertDICDefinitionMethodCallOnce($definition, 'setSQLLogger', array(new Reference('doctrine.dbal.logger.chain.profile'))); + + $definition = $container->getDefinition('doctrine.dbal.both_connection.configuration'); + $this->assertDICDefinitionMethodCallOnce($definition, 'setSQLLogger', array(new Reference('doctrine.dbal.logger.chain.both'))); + } + + public function testBundleEntityAliases() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + + $config = $this->getConnectionConfig(); + $config['orm'] = array('default_entity_manager' => 'default', 'entity_managers' => array('default' => array('mappings' => array('YamlBundle' => array())))); + $loader->load(array($config), $container); + + $definition = $container->getDefinition('doctrine.orm.default_configuration'); + $this->assertDICDefinitionMethodCallOnce($definition, 'setEntityNamespaces', + array(array('YamlBundle' => 'Fixtures\Bundles\YamlBundle\Entity')) + ); + } + + public function testOverwriteEntityAliases() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + + $config = $this->getConnectionConfig(); + $config['orm'] = array('default_entity_manager' => 'default', 'entity_managers' => array('default' => array('mappings' => array('YamlBundle' => array('alias' => 'yml'))))); + $loader->load(array($config), $container); + + $definition = $container->getDefinition('doctrine.orm.default_configuration'); + $this->assertDICDefinitionMethodCallOnce($definition, 'setEntityNamespaces', + array(array('yml' => 'Fixtures\Bundles\YamlBundle\Entity')) + ); + } + + public function testYamlBundleMappingDetection() + { + $container = $this->getContainer('YamlBundle'); + $loader = new DoctrineExtension(); + + $config = $this->getConnectionConfig(); + $config['orm'] = array('default_entity_manager' => 'default', 'entity_managers' => array('default' => array('mappings' => array('YamlBundle' => array())))); + $loader->load(array($config), $container); + + $definition = $container->getDefinition('doctrine.orm.default_metadata_driver'); + $this->assertDICDefinitionMethodCallOnce($definition, 'addDriver', array( + new Reference('doctrine.orm.default_yml_metadata_driver'), + 'Fixtures\Bundles\YamlBundle\Entity' + )); + } + + public function testXmlBundleMappingDetection() + { + $container = $this->getContainer('XmlBundle'); + $loader = new DoctrineExtension(); + + $config = $this->getConnectionConfig(); + $config['orm'] = array('default_entity_manager' => 'default', 'entity_managers' => array('default' => array('mappings' => array('XmlBundle' => array())))); + $loader->load(array($config), $container); + + $definition = $container->getDefinition('doctrine.orm.default_metadata_driver'); + $this->assertDICDefinitionMethodCallOnce($definition, 'addDriver', array( + new Reference('doctrine.orm.default_xml_metadata_driver'), + 'Fixtures\Bundles\XmlBundle\Entity' + )); + } + + public function testAnnotationsBundleMappingDetection() + { + $container = $this->getContainer('AnnotationsBundle'); + $loader = new DoctrineExtension(); + + $config = $this->getConnectionConfig(); + $config['orm'] = array('default_entity_manager' => 'default', 'entity_managers' => array('default' => array('mappings' => array('AnnotationsBundle' => array())))); + $loader->load(array($config), $container); + + $definition = $container->getDefinition('doctrine.orm.default_metadata_driver'); + $this->assertDICDefinitionMethodCallOnce($definition, 'addDriver', array( + new Reference('doctrine.orm.default_annotation_metadata_driver'), + 'Fixtures\Bundles\AnnotationsBundle\Entity' + )); + } + + public function testOrmMergeConfigs() + { + $container = $this->getContainer(array('XmlBundle', 'AnnotationsBundle')); + $loader = new DoctrineExtension(); + + $config1 = $this->getConnectionConfig(); + $config1['orm'] = array( + 'auto_generate_proxy_classes' => true, + 'default_entity_manager' => 'default', + 'entity_managers' => array( + 'default' => array('mappings' => array('AnnotationsBundle' => array())) + )); + $config2 = $this->getConnectionConfig(); + $config2['orm'] = array( + 'auto_generate_proxy_classes' => false, + 'default_entity_manager' => 'default', + 'entity_managers' => array( + 'default' => array('mappings' => array('XmlBundle' => array())) + )); + $loader->load(array($config1, $config2), $container); + + $definition = $container->getDefinition('doctrine.orm.default_metadata_driver'); + $this->assertDICDefinitionMethodCallAt(0, $definition, 'addDriver', array( + new Reference('doctrine.orm.default_annotation_metadata_driver'), + 'Fixtures\Bundles\AnnotationsBundle\Entity' + )); + $this->assertDICDefinitionMethodCallAt(1, $definition, 'addDriver', array( + new Reference('doctrine.orm.default_xml_metadata_driver'), + 'Fixtures\Bundles\XmlBundle\Entity' + )); + + $configDef = $container->getDefinition('doctrine.orm.default_configuration'); + $this->assertDICDefinitionMethodCallOnce($configDef, 'setAutoGenerateProxyClasses'); + + $calls = $configDef->getMethodCalls(); + foreach ($calls as $call) { + if ($call[0] == 'setAutoGenerateProxyClasses') { + $this->assertFalse($container->getParameterBag()->resolveValue($call[1][0])); + break; + } + } + } + + public function testEntityManagerMetadataCacheDriverConfiguration() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + + $this->loadFromFile($container, 'orm_service_multiple_entity_managers'); + + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.orm.em1_metadata_cache'); + $this->assertDICDefinitionClass($definition, '%doctrine.orm.cache.xcache.class%'); + + $definition = $container->getDefinition('doctrine.orm.em2_metadata_cache'); + $this->assertDICDefinitionClass($definition, '%doctrine.orm.cache.apc.class%'); + } + + public function testEntityManagerMemcacheMetadataCacheDriverConfiguration() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + + $this->loadFromFile($container, 'orm_service_simple_single_entity_manager'); + + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.orm.default_metadata_cache'); + $this->assertDICDefinitionClass($definition, 'Doctrine\Common\Cache\MemcacheCache'); + $this->assertDICDefinitionMethodCallOnce($definition, 'setMemcache', + array(new Reference('doctrine.orm.default_memcache_instance')) + ); + + $definition = $container->getDefinition('doctrine.orm.default_memcache_instance'); + $this->assertDICDefinitionClass($definition, 'Memcache'); + $this->assertDICDefinitionMethodCallOnce($definition, 'connect', array( + 'localhost', '11211' + )); + } + + public function testDependencyInjectionImportsOverrideDefaults() + { + $container = $this->getContainer(); + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + + $this->loadFromFile($container, 'orm_imports'); + + $this->compileContainer($container); + + $cacheDefinition = $container->getDefinition('doctrine.orm.default_metadata_cache'); + $this->assertEquals('%doctrine.orm.cache.apc.class%', $cacheDefinition->getClass()); + + $configDefinition = $container->getDefinition('doctrine.orm.default_configuration'); + $this->assertDICDefinitionMethodCallOnce($configDefinition, 'setAutoGenerateProxyClasses', array('%doctrine.orm.auto_generate_proxy_classes%')); + } + + public function testSingleEntityManagerMultipleMappingBundleDefinitions() + { + $container = $this->getContainer(array('YamlBundle', 'AnnotationsBundle', 'XmlBundle')); + + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + + $this->loadFromFile($container, 'orm_single_em_bundle_mappings'); + + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.orm.default_metadata_driver'); + + $this->assertDICDefinitionMethodCallAt(0, $definition, 'addDriver', array( + new Reference('doctrine.orm.default_annotation_metadata_driver'), + 'Fixtures\Bundles\AnnotationsBundle\Entity' + )); + + $this->assertDICDefinitionMethodCallAt(1, $definition, 'addDriver', array( + new Reference('doctrine.orm.default_yml_metadata_driver'), + 'Fixtures\Bundles\YamlBundle\Entity' + )); + + $this->assertDICDefinitionMethodCallAt(2, $definition, 'addDriver', array( + new Reference('doctrine.orm.default_xml_metadata_driver'), + 'Fixtures\Bundles\XmlBundle' + )); + + $annDef = $container->getDefinition('doctrine.orm.default_annotation_metadata_driver'); + $this->assertDICConstructorArguments($annDef, array( + new Reference('doctrine.orm.metadata.annotation_reader'), + array(__DIR__ .DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'Bundles'.DIRECTORY_SEPARATOR.'AnnotationsBundle'.DIRECTORY_SEPARATOR.'Entity') + )); + + $ymlDef = $container->getDefinition('doctrine.orm.default_yml_metadata_driver'); + $this->assertDICConstructorArguments($ymlDef, array( + array(__DIR__ .DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'Bundles'.DIRECTORY_SEPARATOR.'YamlBundle'.DIRECTORY_SEPARATOR.'Resources'.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'doctrine' => 'Fixtures\Bundles\YamlBundle\Entity') + )); + + $xmlDef = $container->getDefinition('doctrine.orm.default_xml_metadata_driver'); + $this->assertDICConstructorArguments($xmlDef, array( + array(__DIR__ .DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'Bundles'.DIRECTORY_SEPARATOR.'XmlBundle'.DIRECTORY_SEPARATOR.'Resources'.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'doctrine' => 'Fixtures\Bundles\XmlBundle') + )); + } + + public function testMultipleEntityManagersMappingBundleDefinitions() + { + $container = $this->getContainer(array('YamlBundle', 'AnnotationsBundle', 'XmlBundle')); + + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + + $this->loadFromFile($container, 'orm_multiple_em_bundle_mappings'); + + $this->compileContainer($container); + + $this->assertEquals(array('em1' => 'doctrine.orm.em1_entity_manager', 'em2' => 'doctrine.orm.em2_entity_manager'), $container->getParameter('doctrine.entity_managers'), "Set of the existing EntityManagers names is incorrect."); + $this->assertEquals('%doctrine.entity_managers%', $container->getDefinition('doctrine')->getArgument(2), "Set of the existing EntityManagers names is incorrect."); + + $def1 = $container->getDefinition('doctrine.orm.em1_metadata_driver'); + $def2 = $container->getDefinition('doctrine.orm.em2_metadata_driver'); + + $this->assertDICDefinitionMethodCallAt(0, $def1, 'addDriver', array( + new Reference('doctrine.orm.em1_annotation_metadata_driver'), + 'Fixtures\Bundles\AnnotationsBundle\Entity' + )); + + $this->assertDICDefinitionMethodCallAt(0, $def2, 'addDriver', array( + new Reference('doctrine.orm.em2_yml_metadata_driver'), + 'Fixtures\Bundles\YamlBundle\Entity' + )); + + $this->assertDICDefinitionMethodCallAt(1, $def2, 'addDriver', array( + new Reference('doctrine.orm.em2_xml_metadata_driver'), + 'Fixtures\Bundles\XmlBundle' + )); + + $annDef = $container->getDefinition('doctrine.orm.em1_annotation_metadata_driver'); + $this->assertDICConstructorArguments($annDef, array( + new Reference('doctrine.orm.metadata.annotation_reader'), + array(__DIR__ .DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'Bundles'.DIRECTORY_SEPARATOR.'AnnotationsBundle'.DIRECTORY_SEPARATOR.'Entity') + )); + + $ymlDef = $container->getDefinition('doctrine.orm.em2_yml_metadata_driver'); + $this->assertDICConstructorArguments($ymlDef, array( + array(__DIR__ .DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'Bundles'.DIRECTORY_SEPARATOR.'YamlBundle'.DIRECTORY_SEPARATOR.'Resources'.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'doctrine' => 'Fixtures\Bundles\YamlBundle\Entity') + )); + + $xmlDef = $container->getDefinition('doctrine.orm.em2_xml_metadata_driver'); + $this->assertDICConstructorArguments($xmlDef, array( + array(__DIR__ .DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'Bundles'.DIRECTORY_SEPARATOR.'XmlBundle'.DIRECTORY_SEPARATOR.'Resources'.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'doctrine' => 'Fixtures\Bundles\XmlBundle') + )); + } + + public function testAnnotationsBundleMappingDetectionWithVendorNamespace() + { + $container = $this->getContainer('AnnotationsBundle', 'Vendor'); + $loader = new DoctrineExtension(); + + $config = $this->getConnectionConfig(); + $config['orm'] = array('default_entity_manager' => 'default', 'entity_managers' => array('default' => array('mappings' => array('AnnotationsBundle' => array())))); + $loader->load(array($config), $container); + + $calls = $container->getDefinition('doctrine.orm.default_metadata_driver')->getMethodCalls(); + $this->assertEquals('doctrine.orm.default_annotation_metadata_driver', (string) $calls[0][1][0]); + $this->assertEquals('Fixtures\Bundles\Vendor\AnnotationsBundle\Entity', $calls[0][1][1]); + } + + public function testSetTypes() + { + $container = $this->getContainer(array('YamlBundle')); + + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + $this->loadFromFile($container, 'dbal_types'); + $this->compileContainer($container); + + $this->assertEquals( + array('test' => array('class' => 'Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestType', 'commented' => true)), + $container->getParameter('doctrine.dbal.connection_factory.types') + ); + $this->assertEquals('%doctrine.dbal.connection_factory.types%', $container->getDefinition('doctrine.dbal.connection_factory')->getArgument(0)); + } + + public function testSetCustomFunctions() + { + $container = $this->getContainer(array('YamlBundle')); + + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + $this->loadFromFile($container, 'orm_functions'); + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.orm.default_configuration'); + $this->assertDICDefinitionMethodCallOnce($definition, 'addCustomStringFunction', array('test_string', 'Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestStringFunction')); + $this->assertDICDefinitionMethodCallOnce($definition, 'addCustomNumericFunction', array('test_numeric', 'Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestNumericFunction')); + $this->assertDICDefinitionMethodCallOnce($definition, 'addCustomDatetimeFunction', array('test_datetime', 'Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestDatetimeFunction')); + } + + public function testSingleEMSetCustomFunctions() + { + $container = $this->getContainer(array('YamlBundle')); + + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + $this->loadFromFile($container, 'orm_single_em_dql_functions'); + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.orm.default_configuration'); + $this->assertDICDefinitionMethodCallOnce($definition, 'addCustomStringFunction', array('test_string', 'Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestStringFunction')); + } + + public function testAddCustomHydrationMode() + { + $container = $this->getContainer(array('YamlBundle')); + + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + $this->loadFromFile($container, 'orm_hydration_mode'); + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.orm.default_configuration'); + $this->assertDICDefinitionMethodCallOnce($definition, 'addCustomHydrationMode', array('test_hydrator', 'Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestHydrator')); + } + + public function testAddFilter() + { + $container = $this->getContainer(array('YamlBundle')); + + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + + $this->loadFromFile($container, 'orm_filters'); + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.orm.default_configuration'); + $this->assertDICDefinitionMethodCallOnce($definition, 'addFilter', array('soft_delete', 'Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\TestFilter')); + + $definition = $container->getDefinition('doctrine.orm.default_manager_configurator'); + $this->assertDICConstructorArguments($definition, array(array('soft_delete'))); + + // Let's create the instance to check the configurator work. + /** @var $entityManager \Doctrine\ORM\EntityManager */ + $entityManager = $container->get('doctrine.orm.entity_manager'); + $this->assertCount(1, $entityManager->getFilters()->getEnabledFilters()); + } + + public function testResolveTargetEntity() + { + $container = $this->getContainer(array('YamlBundle')); + + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + + $this->loadFromFile($container, 'orm_resolve_target_entity'); + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.orm.listeners.resolve_target_entity'); + $this->assertDICDefinitionMethodCallOnce($definition, 'addResolveTargetEntity', array('Symfony\Component\Security\Core\User\UserInterface', 'MyUserClass', array())); + $this->assertEquals(array('doctrine.event_listener' => array( array('event' => 'loadClassMetadata') ) ), $definition->getTags()); + } + + protected function getContainer($bundles = 'YamlBundle', $vendor = null) + { + $bundles = (array) $bundles; + + $map = array(); + foreach ($bundles as $bundle) { + require_once __DIR__.'/Fixtures/Bundles/'.($vendor ? $vendor.'/' : '').$bundle.'/'.$bundle.'.php'; + + $map[$bundle] = 'Fixtures\\Bundles\\'.($vendor ? $vendor.'\\' : '').$bundle.'\\'.$bundle; + } + + return new ContainerBuilder(new ParameterBag(array( + 'kernel.debug' => false, + 'kernel.bundles' => $map, + 'kernel.cache_dir' => sys_get_temp_dir(), + 'kernel.environment' => 'test', + 'kernel.root_dir' => __DIR__.'/../../' // src dir + ))); + } + + /** + * Assertion on the Class of a DIC Service Definition. + * + * @param \Symfony\Component\DependencyInjection\Definition $definition + * @param string $expectedClass + */ + protected function assertDICDefinitionClass($definition, $expectedClass) + { + $this->assertEquals($expectedClass, $definition->getClass(), 'Expected Class of the DIC Container Service Definition is wrong.'); + } + + protected function assertDICConstructorArguments($definition, $args) + { + $this->assertEquals($args, $definition->getArguments(), "Expected and actual DIC Service constructor arguments of definition '".$definition->getClass()."' don't match."); + } + + protected function assertDICDefinitionMethodCallAt($pos, $definition, $methodName, array $params = null) + { + $calls = $definition->getMethodCalls(); + if (isset($calls[$pos][0])) { + $this->assertEquals($methodName, $calls[$pos][0], "Method '".$methodName."' is expected to be called at position $pos."); + + if ($params !== null) { + $this->assertEquals($params, $calls[$pos][1], "Expected parameters to methods '".$methodName."' do not match the actual parameters."); + } + } + } + + /** + * Assertion for the DI Container, check if the given definition contains a method call with the given parameters. + * + * @param \Symfony\Component\DependencyInjection\Definition $definition + * @param string $methodName + * @param array $params + */ + protected function assertDICDefinitionMethodCallOnce($definition, $methodName, array $params = null) + { + $calls = $definition->getMethodCalls(); + $called = false; + foreach ($calls as $call) { + if ($call[0] == $methodName) { + if ($called) { + $this->fail("Method '".$methodName."' is expected to be called only once, a second call was registered though."); + } else { + $called = true; + if ($params !== null) { + $this->assertEquals($params, $call[1], "Expected parameters to methods '".$methodName."' do not match the actual parameters."); + } + } + } + } + if (!$called) { + $this->fail("Method '".$methodName."' is expected to be called once, definition does not contain a call though."); + } + } + + protected function compileContainer(ContainerBuilder $container) + { + $container->getCompilerPassConfig()->setOptimizationPasses(array(new ResolveDefinitionTemplatesPass())); + $container->getCompilerPassConfig()->setRemovingPasses(array()); + $container->compile(); + } + + protected function getConnectionConfig() + { + return array('dbal' => array('connections' => array('default' => array('password' => 'foo')))); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/AnnotationsBundle.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/AnnotationsBundle.php new file mode 100644 index 0000000..c99a0bb --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/AnnotationsBundle.php @@ -0,0 +1,21 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\AnnotationsBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class AnnotationsBundle extends Bundle +{ +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/Entity/Test.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/Entity/Test.php new file mode 100644 index 0000000..9c2932c --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/Entity/Test.php @@ -0,0 +1,19 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\AnnotationsBundle\Entity; + +class Test +{ +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/AnnotationsBundle.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/AnnotationsBundle.php new file mode 100644 index 0000000..fe940e3 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/AnnotationsBundle.php @@ -0,0 +1,21 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\Vendor\AnnotationsBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class AnnotationsBundle extends Bundle +{ +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/Entity/Test.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/Entity/Test.php new file mode 100644 index 0000000..0dfe0f6 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/Vendor/AnnotationsBundle/Entity/Test.php @@ -0,0 +1,19 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\Vendor\AnnotationsBundle\Entity; + +class Test +{ +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/Entity/Test.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/Entity/Test.php new file mode 100644 index 0000000..770ab31 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/Entity/Test.php @@ -0,0 +1,19 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\XmlBundle\Entity; + +class Test +{ +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/Resources/config/doctrine/Test.orm.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/Resources/config/doctrine/Test.orm.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/XmlBundle.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/XmlBundle.php new file mode 100644 index 0000000..f14e909 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/XmlBundle.php @@ -0,0 +1,21 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\XmlBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class XmlBundle extends Bundle +{ +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/Entity/Test.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/Entity/Test.php new file mode 100644 index 0000000..9eb004c --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/Entity/Test.php @@ -0,0 +1,20 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\YamlBundle\Entity; + +class Test +{ + private $id; +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/Resources/config/doctrine/Test.orm.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/Resources/config/doctrine/Test.orm.yml new file mode 100644 index 0000000..6ac8c14 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/Resources/config/doctrine/Test.orm.yml @@ -0,0 +1,5 @@ +Fixtures\Bundles\YamlBundle\Entity\Test: + type: entity + id: + id: + type: integer diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php new file mode 100644 index 0000000..8b9fea0 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php @@ -0,0 +1,21 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\YamlBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class YamlBundle extends Bundle +{ +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_logging.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_logging.xml new file mode 100644 index 0000000..b7b17b4 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_logging.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_service_multiple_connections.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_service_multiple_connections.xml new file mode 100644 index 0000000..acfa896 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_service_multiple_connections.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_service_single_connection.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_service_single_connection.xml new file mode 100644 index 0000000..fada4ad --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_service_single_connection.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_service_single_master_slave_connection.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_service_single_master_slave_connection.xml new file mode 100644 index 0000000..1b31a4c --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_service_single_master_slave_connection.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_types.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_types.xml new file mode 100644 index 0000000..368cc89 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/dbal_types.xml @@ -0,0 +1,15 @@ + + + + + + + Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestType + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_filters.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_filters.xml new file mode 100644 index 0000000..913a10e --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_filters.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\TestFilter + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_functions.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_functions.xml new file mode 100644 index 0000000..7bbdf7a --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_functions.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestStringFunction + Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestNumericFunction + Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestDatetimeFunction + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_hydration_mode.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_hydration_mode.xml new file mode 100644 index 0000000..b6ceb83 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_hydration_mode.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestHydrator + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_imports.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_imports.xml new file mode 100644 index 0000000..20e9b25 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_imports.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_imports_import.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_imports_import.xml new file mode 100644 index 0000000..de649bf --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_imports_import.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_multiple_em_bundle_mappings.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_multiple_em_bundle_mappings.xml new file mode 100644 index 0000000..65312da --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_multiple_em_bundle_mappings.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_proxy.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_proxy.xml new file mode 100644 index 0000000..96208dd --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_proxy.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_resolve_target_entity.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_resolve_target_entity.xml new file mode 100644 index 0000000..6890cd0 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_resolve_target_entity.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + MyUserClass + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_service_multiple_entity_managers.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_service_multiple_entity_managers.xml new file mode 100644 index 0000000..9c3b840 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_service_multiple_entity_managers.xml @@ -0,0 +1,40 @@ + + + + + + Proxies + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_service_simple_single_entity_manager.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_service_simple_single_entity_manager.xml new file mode 100644 index 0000000..0870542 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_service_simple_single_entity_manager.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + Doctrine\Common\Cache\MemcacheCache + localhost + 11211 + Memcache + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_service_single_entity_manager.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_service_single_entity_manager.xml new file mode 100644 index 0000000..bd64743 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_service_single_entity_manager.xml @@ -0,0 +1,39 @@ + + + + + + Proxies + + + + + + + + + + + Doctrine\Common\Cache\MemcacheCache + localhost + 11211 + Memcache + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_single_em_bundle_mappings.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_single_em_bundle_mappings.xml new file mode 100644 index 0000000..5ea5b2f --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_single_em_bundle_mappings.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_single_em_dql_functions.xml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_single_em_dql_functions.xml new file mode 100644 index 0000000..0ed15d1 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/xml/orm_single_em_dql_functions.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestStringFunction + + + + diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_logging.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_logging.yml new file mode 100644 index 0000000..ee8ef26 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_logging.yml @@ -0,0 +1,13 @@ +doctrine: + dbal: + default_connection: mysql + connections: + log: + logging: true + profiling: false + profile: + logging: false + profiling: true + both: + logging: true + profiling: true diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_service_multiple_connections.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_service_multiple_connections.yml new file mode 100644 index 0000000..a01101d --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_service_multiple_connections.yml @@ -0,0 +1,18 @@ +doctrine: + dbal: + default_connection: mysql + connections: + mysql: + dbname: mysql_db + user: mysql_user + password: mysql_s3cr3t + unix_socket: /path/to/mysqld.sock + sqlite: + driver: pdo_sqlite + memory: true + oci: + driver: oci8 + dbname: oracle_db + user: oracle_user + password: oracle_s3cr3t + charset: utf8 diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_service_single_connection.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_service_single_connection.yml new file mode 100644 index 0000000..9db7c37 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_service_single_connection.yml @@ -0,0 +1,6 @@ +doctrine: + dbal: + dbname: mysql_db + user: mysql_user + password: mysql_s3cr3t + unix_socket: /path/to/mysqld.sock diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_service_single_master_slave_connection.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_service_single_master_slave_connection.yml new file mode 100644 index 0000000..c7f05f6 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_service_single_master_slave_connection.yml @@ -0,0 +1,12 @@ +doctrine: + dbal: + dbname: mysql_db + user: mysql_user + password: mysql_s3cr3t + unix_socket: /path/to/mysqld.sock + slaves: + slave1: + user: slave_user + dbname: slave_db + password: slave_s3cr3t + unix_socket: /path/to/mysqld_slave.sock diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_types.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_types.yml new file mode 100644 index 0000000..1e86dc4 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/dbal_types.yml @@ -0,0 +1,7 @@ +doctrine: + dbal: + default_connection: default + types: + test: Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestType + connections: + default: ~ diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_filters.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_filters.yml new file mode 100644 index 0000000..aa85341 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_filters.yml @@ -0,0 +1,12 @@ +doctrine: + dbal: + default_connection: default + connections: + default: + dbname: db + + orm: + filters: + soft_delete: + class: Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\TestFilter + enabled: true diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_functions.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_functions.yml new file mode 100644 index 0000000..2185305 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_functions.yml @@ -0,0 +1,19 @@ +doctrine: + dbal: + default_connection: default + connections: + default: + dbname: db + + orm: + entity_managers: + default: + mappings: + YamlBundle: ~ + dql: + string_functions: + test_string: Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestStringFunction + numeric_functions: + test_numeric: Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestNumericFunction + datetime_functions: + test_datetime: Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestDatetimeFunction diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_hydration_mode.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_hydration_mode.yml new file mode 100644 index 0000000..6d81a5b --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_hydration_mode.yml @@ -0,0 +1,14 @@ +doctrine: + dbal: + default_connection: default + connections: + default: + dbname: db + + orm: + entity_managers: + default: + hydrators: + test_hydrator: Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestHydrator + mappings: + YamlBundle: ~ diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_imports.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_imports.yml new file mode 100644 index 0000000..df87945 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_imports.yml @@ -0,0 +1,6 @@ +imports: + - { resource: orm_imports_import.yml } + +doctrine: + orm: + auto_generate_proxy_classes: true diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_imports_import.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_imports_import.yml new file mode 100644 index 0000000..c533c1e --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_imports_import.yml @@ -0,0 +1,15 @@ +doctrine: + dbal: + default_connection: default + connections: + default: + dbname: db + + orm: + auto_generate_proxy_classes: false + default_entity_manager: default + entity_managers: + default: + metadata_cache_driver: apc + mappings: + YamlBundle: ~ diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_multiple_em_bundle_mappings.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_multiple_em_bundle_mappings.yml new file mode 100644 index 0000000..320df94 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_multiple_em_bundle_mappings.yml @@ -0,0 +1,23 @@ +doctrine: + dbal: + default_connection: default + connections: + default: + dbname: db + + orm: + default_entity_manager: em2 + entity_managers: + em1: + mappings: + AnnotationsBundle: ~ + em2: + mappings: + YamlBundle: + dir: Resources/config/doctrine + alias: yml + manual: + type: xml + prefix: Fixtures\Bundles\XmlBundle + dir: %kernel.root_dir%/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/Resources/config/doctrine + alias: TestAlias diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_resolve_target_entity.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_resolve_target_entity.yml new file mode 100644 index 0000000..4bdbedd --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_resolve_target_entity.yml @@ -0,0 +1,10 @@ +doctrine: + dbal: + default_connection: default + connections: + default: + dbname: db + + orm: + resolve_target_entities: + Symfony\Component\Security\Core\User\UserInterface: MyUserClass diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_service_multiple_entity_managers.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_service_multiple_entity_managers.yml new file mode 100644 index 0000000..31b9cc1 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_service_multiple_entity_managers.yml @@ -0,0 +1,34 @@ +parameters: + doctrine.orm.proxy_namespace: Proxies + +doctrine: + dbal: + default_connection: conn1 + connections: + conn1: + driver: pdo_sqlite + dbname: sqlite_db + user: sqlite_user + password: sqlite_s3cr3t + memory: true + conn2: + driver: pdo_sqlite + dbname: sqlite_db + user: sqlite_user + password: sqlite_s3cr3t + memory: true + + orm: + default_entity_manager: em2 + auto_generate_proxy_classes: true + entity_managers: + em1: + metadata_cache_driver: xcache + connection: conn1 + mappings: + YamlBundle: ~ + em2: + metadata_cache_driver: apc + connection: conn2 + mappings: + YamlBundle: ~ diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_service_simple_single_entity_manager.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_service_simple_single_entity_manager.yml new file mode 100644 index 0000000..b2082a5 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_service_simple_single_entity_manager.yml @@ -0,0 +1,19 @@ +doctrine: + dbal: + default_connection: default + connections: + default: + dbname: db + + orm: + default_entity_manager: default + entity_managers: + default: + mappings: + YamlBundle: ~ + metadata_cache_driver: + type: memcache + class: Doctrine\Common\Cache\MemcacheCache + host: localhost + port: 11211 + instance_class: Memcache diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_service_single_entity_manager.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_service_single_entity_manager.yml new file mode 100644 index 0000000..9f81981 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_service_single_entity_manager.yml @@ -0,0 +1,27 @@ +doctrine: + dbal: + default_connection: default + connections: + default: + driver: pdo_sqlite + dbname: sqlite_db + user: sqlite_user + password: sqlite_s3cr3t + memory: true + + orm: + default_entity_manager: dm2 + proxy_namespace: Proxies + auto_generate_proxy_classes: true + entity_managers: + default: + connection: default + default_repository_class: Acme\Doctrine\Repository + mappings: + YamlBundle: ~ + metadata_cache_driver: + type: memcache + class: Doctrine\Common\Cache\MemcacheCache + host: localhost + port: 11211 + instance_class: Memcache diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_single_em_bundle_mappings.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_single_em_bundle_mappings.yml new file mode 100644 index 0000000..bf4a199 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_single_em_bundle_mappings.yml @@ -0,0 +1,18 @@ +doctrine: + dbal: + default_connection: default + connections: + default: + dbname: db + + orm: + mappings: + AnnotationsBundle: ~ + YamlBundle: + dir: Resources/config/doctrine + alias: yml + manual: + type: xml + prefix: Fixtures\Bundles\XmlBundle + dir: %kernel.root_dir%/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/Resources/config/doctrine + alias: TestAlias diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_single_em_dql_functions.yml b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_single_em_dql_functions.yml new file mode 100644 index 0000000..9bcec3c --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/Fixtures/config/yml/orm_single_em_dql_functions.yml @@ -0,0 +1,11 @@ +doctrine: + dbal: + default_connection: default + connections: + default: + dbname: db + + orm: + dql: + string_functions: + test_string: Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestStringFunction diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestDatetimeFunction.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestDatetimeFunction.php new file mode 100644 index 0000000..02ed7e0 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestDatetimeFunction.php @@ -0,0 +1,32 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection; + +use Doctrine\ORM\Query\AST\Functions\FunctionNode; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\Parser; + +class TestDatetimeFunction extends FunctionNode +{ + public function getSql(SqlWalker $sqlWalker) + { + return ''; + } + + public function parse(Parser $parser) + { + return ''; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestFilter.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestFilter.php new file mode 100644 index 0000000..63b5bee --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestFilter.php @@ -0,0 +1,30 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection; + +use Doctrine\ORM\Query\Filter\SQLFilter; +use Doctrine\ORM\Mapping\ClassMetadata; + +class TestFilter extends SQLFilter +{ + /** + * Gets the SQL query part to add to a query. + * + * @return string The constraint SQL if there is available, empty string otherwise + */ + public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) + { + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestNumericFunction.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestNumericFunction.php new file mode 100644 index 0000000..dd5d865 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestNumericFunction.php @@ -0,0 +1,32 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection; + +use Doctrine\ORM\Query\AST\Functions\FunctionNode; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\Parser; + +class TestNumericFunction extends FunctionNode +{ + public function getSql(SqlWalker $sqlWalker) + { + return ''; + } + + public function parse(Parser $parser) + { + return ''; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestStringFunction.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestStringFunction.php new file mode 100644 index 0000000..8831482 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestStringFunction.php @@ -0,0 +1,32 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection; + +use Doctrine\ORM\Query\AST\Functions\FunctionNode; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\Parser; + +class TestStringFunction extends FunctionNode +{ + public function getSql(SqlWalker $sqlWalker) + { + return ''; + } + + public function parse(Parser $parser) + { + return ''; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestType.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestType.php new file mode 100644 index 0000000..18f6b0f --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/TestType.php @@ -0,0 +1,30 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +class TestType extends \Doctrine\DBAL\Types\Type +{ + public function getName() + { + return 'test'; + } + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return ''; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/XMLSchemaTest.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/XMLSchemaTest.php new file mode 100644 index 0000000..c48a68b --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/XMLSchemaTest.php @@ -0,0 +1,66 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection; + +class XMLSchemaTest extends \PHPUnit_Framework_TestCase +{ + static public function dataValidateSchemaFiles() + { + $schemaFiles = array(); + $di = new \DirectoryIterator(__DIR__."/Fixtures/config/xml"); + foreach ($di as $element) { + if ($element->isFile() && substr($element->getFilename(), -4) === ".xml") { + $schemaFiles[] = array($element->getPathname()); + } + } + + return $schemaFiles; + } + + /** + * @dataProvider dataValidateSchemaFiles + */ + public function testValidateSchema($file) + { + $found = false; + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->load($file); + + + $dbalElements = $dom->getElementsByTagNameNS("http://symfony.com/schema/dic/doctrine", "config"); + if ($dbalElements->length) { + $dbalDom = new \DOMDocument('1.0', 'UTF-8'); + $dbalNode = $dbalDom->importNode($dbalElements->item(0)); + $dbalDom->appendChild($dbalNode); + + $ret = $dbalDom->schemaValidate(__DIR__."/../../Resources/config/schema/doctrine-1.0.xsd"); + $this->assertTrue($ret, "DoctrineBundle Dependency Injection XMLSchema did not validate this XML instance."); + $found = true; + } + + $ormElements = $dom->getElementsByTagNameNS("http://symfony.com/schema/dic/doctrine", "config"); + if ($ormElements->length) { + $ormDom = new \DOMDocument('1.0', 'UTF-8'); + $ormNode = $ormDom->importNode($ormElements->item(0)); + $ormDom->appendChild($ormNode); + + $ret = $ormDom->schemaValidate(__DIR__."/../../Resources/config/schema/doctrine-1.0.xsd"); + $this->assertTrue($ret, "DoctrineBundle Dependency Injection XMLSchema did not validate this XML instance."); + $found = true; + } + + $this->assertTrue($found, "Neither nor elements found in given XML. Are namespaces configured correctly?"); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/XmlDoctrineExtensionTest.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/XmlDoctrineExtensionTest.php new file mode 100644 index 0000000..4b787fb --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/XmlDoctrineExtensionTest.php @@ -0,0 +1,28 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\Config\FileLocator; + +class XmlDoctrineExtensionTest extends AbstractDoctrineExtensionTest +{ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loadXml = new XmlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/config/xml')); + $loadXml->load($file.'.xml'); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/YamlDoctrineExtensionTest.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/YamlDoctrineExtensionTest.php new file mode 100644 index 0000000..2a96bd5 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/DependencyInjection/YamlDoctrineExtensionTest.php @@ -0,0 +1,28 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\Config\FileLocator; + +class YamlDoctrineExtensionTest extends AbstractDoctrineExtensionTest +{ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loadYaml = new YamlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/config/yml')); + $loadYaml->load($file.'.yml'); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/Mapping/MetadataFactoryTest.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/Mapping/MetadataFactoryTest.php new file mode 100644 index 0000000..b226336 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/Mapping/MetadataFactoryTest.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Tests; + +use Doctrine\Bundle\DoctrineBundle\Tests\TestCase; +use Doctrine\Bundle\DoctrineBundle\Mapping\MetadataFactory; +use Doctrine\Bundle\DoctrineBundle\Mapping\ClassMetadataCollection; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +class MetadataFactoryTest extends TestCase +{ + protected function setUp() + { + parent::setUp(); + + if (!class_exists('Doctrine\\ORM\\Version')) { + $this->markTestSkipped('Doctrine ORM is not available.'); + } + } + + public function testFindNamespaceAndPathForMetadata() + { + $class = new ClassMetadataInfo(__CLASS__); + $collection = new ClassMetadataCollection(array($class)); + + $registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $factory = new MetadataFactory($registry); + + $this->setExpectedException("RuntimeException", "Can't find base path for \"Doctrine\Bundle\DoctrineBundle\Tests\MetadataFactoryTest"); + $factory->findNamespaceAndPathForMetadata($collection); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/RegistryTest.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/RegistryTest.php new file mode 100644 index 0000000..9d1003d --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/RegistryTest.php @@ -0,0 +1,149 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Tests; + +use Doctrine\Bundle\DoctrineBundle\Registry; + +class RegistryTest extends TestCase +{ + public function testGetDefaultConnectionName() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $registry = new Registry($container, array(), array(), 'default', 'default'); + + $this->assertEquals('default', $registry->getDefaultConnectionName()); + } + + public function testGetDefaultEntityManagerName() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $registry = new Registry($container, array(), array(), 'default', 'default'); + + $this->assertEquals('default', $registry->getDefaultEntityManagerName()); + } + + public function testGetDefaultConnection() + { + $conn = $this->getMock('Doctrine\DBAL\Connection', array(), array(), '', false); + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container->expects($this->once()) + ->method('get') + ->with($this->equalTo('doctrine.dbal.default_connection')) + ->will($this->returnValue($conn)); + + $registry = new Registry($container, array('default' => 'doctrine.dbal.default_connection'), array(), 'default', 'default'); + + $this->assertSame($conn, $registry->getConnection()); + } + + public function testGetConnection() + { + $conn = $this->getMock('Doctrine\DBAL\Connection', array(), array(), '', false); + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container->expects($this->once()) + ->method('get') + ->with($this->equalTo('doctrine.dbal.default_connection')) + ->will($this->returnValue($conn)); + + $registry = new Registry($container, array('default' => 'doctrine.dbal.default_connection'), array(), 'default', 'default'); + + $this->assertSame($conn, $registry->getConnection('default')); + } + + public function testGetUnknownConnection() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $registry = new Registry($container, array(), array(), 'default', 'default'); + + $this->setExpectedException('InvalidArgumentException', 'Doctrine ORM Connection named "default" does not exist.'); + $registry->getConnection('default'); + } + + public function testGetConnectionNames() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $registry = new Registry($container, array('default' => 'doctrine.dbal.default_connection'), array(), 'default', 'default'); + + $this->assertEquals(array('default' => 'doctrine.dbal.default_connection'), $registry->getConnectionNames()); + } + + public function testGetDefaultEntityManager() + { + $em = new \stdClass(); + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container->expects($this->once()) + ->method('get') + ->with($this->equalTo('doctrine.orm.default_entity_manager')) + ->will($this->returnValue($em)); + + $registry = new Registry($container, array(), array('default' => 'doctrine.orm.default_entity_manager'), 'default', 'default'); + + $this->assertSame($em, $registry->getEntityManager()); + } + + public function testGetEntityManager() + { + $em = new \stdClass(); + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container->expects($this->once()) + ->method('get') + ->with($this->equalTo('doctrine.orm.default_entity_manager')) + ->will($this->returnValue($em)); + + $registry = new Registry($container, array(), array('default' => 'doctrine.orm.default_entity_manager'), 'default', 'default'); + + $this->assertSame($em, $registry->getEntityManager('default')); + } + + public function testGetUnknownEntityManager() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $registry = new Registry($container, array(), array(), 'default', 'default'); + + $this->setExpectedException('InvalidArgumentException', 'Doctrine ORM Manager named "default" does not exist.'); + $registry->getEntityManager('default'); + } + + public function testResetDefaultEntityManager() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container->expects($this->once()) + ->method('set') + ->with($this->equalTo('doctrine.orm.default_entity_manager'), $this->equalTo(null)); + + $registry = new Registry($container, array(), array('default' => 'doctrine.orm.default_entity_manager'), 'default', 'default'); + $registry->resetEntityManager(); + } + + public function testResetEntityManager() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container->expects($this->once()) + ->method('set') + ->with($this->equalTo('doctrine.orm.default_entity_manager'), $this->equalTo(null)); + + $registry = new Registry($container, array(), array('default' => 'doctrine.orm.default_entity_manager'), 'default', 'default'); + $registry->resetEntityManager('default'); + } + + public function testResetUnknownEntityManager() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $registry = new Registry($container, array(), array(), 'default', 'default'); + + $this->setExpectedException('InvalidArgumentException', 'Doctrine ORM Manager named "default" does not exist.'); + $registry->resetEntityManager('default'); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/TestCase.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/TestCase.php new file mode 100644 index 0000000..b8857d8 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/TestCase.php @@ -0,0 +1,83 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Tests; + +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Bundle\DoctrineBundle\DependencyInjection\DoctrineExtension; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass; +use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass; + +class TestCase extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!class_exists('Doctrine\\Common\\Version')) { + $this->markTestSkipped('Doctrine is not available.'); + } + } + + public function createYamlBundleTestContainer() + { + $container = new ContainerBuilder(new ParameterBag(array( + 'kernel.debug' => false, + 'kernel.bundles' => array('YamlBundle' => 'Fixtures\Bundles\YamlBundle\YamlBundle'), + 'kernel.cache_dir' => sys_get_temp_dir(), + 'kernel.environment' => 'test', + 'kernel.root_dir' => __DIR__.'/../../../../' // src dir + ))); + $container->set('annotation_reader', new AnnotationReader()); + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + $loader->load(array(array( + 'dbal' => array( + 'connections' => array( + 'default' => array( + 'driver' => 'pdo_mysql', + 'charset' => 'UTF8', + 'platform-service' => 'my.platform', + ) + ), + 'default_connection' => 'default', + 'types' => array( + 'test' => 'Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestType', + ), + ), 'orm' => array( + 'default_entity_manager' => 'default', + 'entity_managers' => array ( + 'default' => array( + 'mappings' => array('YamlBundle' => array( + 'type' => 'yml', + 'dir' => __DIR__.'/DependencyInjection/Fixtures/Bundles/YamlBundle/Resources/config/doctrine', + 'prefix' => 'Fixtures\Bundles\YamlBundle\Entity', + ) + ))), + 'resolve_target_entities' => array( + 'Symfony\Component\Security\Core\User\UserInterface' => 'stdClass', + ), + ) + )), $container); + + $container->setDefinition('my.platform', new \Symfony\Component\DependencyInjection\Definition('Doctrine\DBAL\Platforms\MySqlPlatform')); + + $container->getCompilerPassConfig()->setOptimizationPasses(array(new ResolveDefinitionTemplatesPass())); + $container->getCompilerPassConfig()->setRemovingPasses(array()); + $container->compile(); + + return $container; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/bootstrap.php b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/bootstrap.php new file mode 100644 index 0000000..2598748 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/Tests/bootstrap.php @@ -0,0 +1,8 @@ +=5.3.2", + "symfony/framework-bundle": "2.1.*", + "symfony/doctrine-bridge": "2.1.*", + "doctrine/dbal": ">=2.2,<2.4-dev" + }, + "require-dev": { + "doctrine/orm": ">=2.2,<2.4-dev", + "symfony/yaml": "2.1.*", + "symfony/validator": "2.1.*" + }, + "suggest": { + "doctrine/orm": "The Doctrine ORM integration is optional in the bundle." + }, + "autoload": { + "psr-0": { "Doctrine\\Bundle\\DoctrineBundle": "" } + }, + "target-dir": "Doctrine/Bundle/DoctrineBundle", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/phpunit.xml.dist b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/phpunit.xml.dist new file mode 100644 index 0000000..d401031 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/phpunit.xml.dist @@ -0,0 +1,35 @@ + + + + + + ./Tests + + + + + + benchmark + + + + + + . + + ./Resources + ./Tests + + + + diff --git a/vendor/doctrine/orm/.travis.yml b/vendor/doctrine/orm/.travis.yml new file mode 100644 index 0000000..716b9b6 --- /dev/null +++ b/vendor/doctrine/orm/.travis.yml @@ -0,0 +1,19 @@ +language: php + +php: + - 5.3 + - 5.4 +env: + - DB=mysql + - DB=pgsql + - DB=sqlite + +before_script: + - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS doctrine_tests;' -U postgres; fi" + - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS doctrine_tests_tmp;' -U postgres; fi" + - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests;' -U postgres; fi" + - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests_tmp;' -U postgres; fi" + - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'create database IF NOT EXISTS doctrine_tests_tmp;create database IF NOT EXISTS doctrine_tests;'; fi" + - git submodule update --init + +script: phpunit --configuration tests/travis/$DB.travis.xml \ No newline at end of file diff --git a/vendor/doctrine/orm/LICENSE b/vendor/doctrine/orm/LICENSE new file mode 100644 index 0000000..1c03f74 --- /dev/null +++ b/vendor/doctrine/orm/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/vendor/doctrine/orm/README.markdown b/vendor/doctrine/orm/README.markdown new file mode 100644 index 0000000..00458ca --- /dev/null +++ b/vendor/doctrine/orm/README.markdown @@ -0,0 +1,18 @@ +# Doctrine 2 ORM + +Master: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=master)](http://travis-ci.org/doctrine/doctrine2) +2.1.x: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.1.x)](http://travis-ci.org/doctrine/doctrine2) + +Doctrine 2 is an object-relational mapper (ORM) for PHP 5.3.2+ that provides transparent persistence +for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features +is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL), +inspired by Hibernates HQL. This provides developers with a powerful alternative to SQL that maintains flexibility +without requiring unnecessary code duplication. + +## More resources: + +* [Website](http://www.doctrine-project.org) +* [Documentation](http://www.doctrine-project.org/projects/orm/2.0/docs/reference/introduction/en) +* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DDC) +* [Downloads](http://github.com/doctrine/doctrine2/downloads) + diff --git a/vendor/doctrine/orm/composer.json b/vendor/doctrine/orm/composer.json new file mode 100644 index 0000000..4f3f7b3 --- /dev/null +++ b/vendor/doctrine/orm/composer.json @@ -0,0 +1,23 @@ +{ + "name": "doctrine/orm", + "type": "library", + "description": "Object-Relational-Mapper for PHP", + "keywords": ["orm", "database"], + "homepage": "http://www.doctrine-project.org", + "license": "LGPL", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"} + ], + "require": { + "php": ">=5.3.2", + "ext-pdo": "*", + "doctrine/common": "2.2.*", + "doctrine/dbal": "2.2.*" + }, + "autoload": { + "psr-0": { "Doctrine\\ORM": "lib/" } + } +} diff --git a/vendor/doctrine/orm/doctrine-mapping.xsd b/vendor/doctrine/orm/doctrine-mapping.xsd new file mode 100644 index 0000000..8db612e --- /dev/null +++ b/vendor/doctrine/orm/doctrine-mapping.xsd @@ -0,0 +1,409 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php b/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php new file mode 100644 index 0000000..0dba9dc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php @@ -0,0 +1,820 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\DBAL\Types\Type, + Doctrine\DBAL\Cache\QueryCacheProfile, + Doctrine\ORM\Query\QueryException, + Doctrine\ORM\Internal\Hydration\CacheHydrator; + +/** + * Base contract for ORM queries. Base class for Query and NativeQuery. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Konsta Vesterinen + */ +abstract class AbstractQuery +{ + /* Hydration mode constants */ + /** + * Hydrates an object graph. This is the default behavior. + */ + const HYDRATE_OBJECT = 1; + /** + * Hydrates an array graph. + */ + const HYDRATE_ARRAY = 2; + /** + * Hydrates a flat, rectangular result set with scalar values. + */ + const HYDRATE_SCALAR = 3; + /** + * Hydrates a single scalar value. + */ + const HYDRATE_SINGLE_SCALAR = 4; + + /** + * Very simple object hydrator (optimized for performance). + */ + const HYDRATE_SIMPLEOBJECT = 5; + + /** + * @var array The parameter map of this query. + */ + protected $_params = array(); + + /** + * @var array The parameter type map of this query. + */ + protected $_paramTypes = array(); + + /** + * @var ResultSetMapping The user-specified ResultSetMapping to use. + */ + protected $_resultSetMapping; + + /** + * @var \Doctrine\ORM\EntityManager The entity manager used by this query object. + */ + protected $_em; + + /** + * @var array The map of query hints. + */ + protected $_hints = array(); + + /** + * @var integer The hydration mode. + */ + protected $_hydrationMode = self::HYDRATE_OBJECT; + + /** + * @param \Doctrine\DBAL\Cache\QueryCacheProfile + */ + protected $_queryCacheProfile; + + /** + * @var boolean Boolean value that indicates whether or not expire the result cache. + */ + protected $_expireResultCache = false; + + /** + * @param \Doctrine\DBAL\Cache\QueryCacheProfile + */ + protected $_hydrationCacheProfile; + + /** + * Initializes a new instance of a class derived from AbstractQuery. + * + * @param \Doctrine\ORM\EntityManager $entityManager + */ + public function __construct(EntityManager $em) + { + $this->_em = $em; + } + + /** + * Retrieves the associated EntityManager of this Query instance. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * Frees the resources used by the query object. + * + * Resets Parameters, Parameter Types and Query Hints. + * + * @return void + */ + public function free() + { + $this->_params = array(); + $this->_paramTypes = array(); + $this->_hints = array(); + } + + /** + * Get all defined parameters. + * + * @return array The defined query parameters. + */ + public function getParameters() + { + return $this->_params; + } + + /** + * Get all defined parameter types. + * + * @return array The defined query parameter types. + */ + public function getParameterTypes() + { + return $this->_paramTypes; + } + + /** + * Gets a query parameter. + * + * @param mixed $key The key (index or name) of the bound parameter. + * @return mixed The value of the bound parameter. + */ + public function getParameter($key) + { + if (isset($this->_params[$key])) { + return $this->_params[$key]; + } + + return null; + } + + /** + * Gets a query parameter type. + * + * @param mixed $key The key (index or name) of the bound parameter. + * @return mixed The parameter type of the bound parameter. + */ + public function getParameterType($key) + { + if (isset($this->_paramTypes[$key])) { + return $this->_paramTypes[$key]; + } + + return null; + } + + /** + * Gets the SQL query that corresponds to this query object. + * The returned SQL syntax depends on the connection driver that is used + * by this query object at the time of this method call. + * + * @return string SQL query + */ + abstract public function getSQL(); + + /** + * Sets a query parameter. + * + * @param string|integer $key The parameter position or name. + * @param mixed $value The parameter value. + * @param string $type The parameter type. If specified, the given value will be run through + * the type conversion of this type. This is usually not needed for + * strings and numeric types. + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setParameter($key, $value, $type = null) + { + $key = trim($key, ':'); + + $value = $this->processParameterValue($value); + if ($type === null) { + $type = Query\ParameterTypeInferer::inferType($value); + } + + $this->_paramTypes[$key] = $type; + $this->_params[$key] = $value; + + return $this; + } + + /** + * Process an individual parameter value + * + * @param mixed $value + * @return array + */ + private function processParameterValue($value) + { + switch (true) { + case is_array($value): + for ($i = 0, $l = count($value); $i < $l; $i++) { + $paramValue = $this->processParameterValue($value[$i]); + $value[$i] = is_array($paramValue) ? $paramValue[key($paramValue)] : $paramValue; + } + + return $value; + + case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value)): + return $this->convertObjectParameterToScalarValue($value); + + default: + return $value; + } + } + + protected function convertObjectParameterToScalarValue($value) + { + $class = $this->_em->getClassMetadata(get_class($value)); + + if ($class->isIdentifierComposite) { + throw new \InvalidArgumentException("Binding an entity with a composite primary key to a query is not supported. You should split the parameter into the explicit fields and bind them seperately."); + } + + if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) { + $values = $this->_em->getUnitOfWork()->getEntityIdentifier($value); + } else { + $values = $class->getIdentifierValues($value); + } + + $value = $values[$class->getSingleIdentifierFieldName()]; + if (!$value) { + throw new \InvalidArgumentException("Binding entities to query parameters only allowed for entities that have an identifier."); + } + + return $value; + } + + /** + * Sets a collection of query parameters. + * + * @param array $params + * @param array $types + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setParameters(array $params, array $types = array()) + { + foreach ($params as $key => $value) { + $this->setParameter($key, $value, isset($types[$key]) ? $types[$key] : null); + } + + return $this; + } + + /** + * Sets the ResultSetMapping that should be used for hydration. + * + * @param ResultSetMapping $rsm + * @return \Doctrine\ORM\AbstractQuery + */ + public function setResultSetMapping(Query\ResultSetMapping $rsm) + { + $this->_resultSetMapping = $rsm; + + return $this; + } + + /** + * Set a cache profile for hydration caching. + * + * If no result cache driver is set in the QueryCacheProfile, the default + * result cache driver is used from the configuration. + * + * Important: Hydration caching does NOT register entities in the + * UnitOfWork when retrieved from the cache. Never use result cached + * entities for requests that also flush the EntityManager. If you want + * some form of caching with UnitOfWork registration you should use + * {@see AbstractQuery::setResultCacheProfile()}. + * + * @example + * $lifetime = 100; + * $resultKey = "abc"; + * $query->setHydrationCacheProfile(new QueryCacheProfile()); + * $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey)); + * + * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile + * @return \Doctrine\ORM\AbstractQuery + */ + public function setHydrationCacheProfile(QueryCacheProfile $profile = null) + { + if ( ! $profile->getResultCacheDriver()) { + $resultCacheDriver = $this->_em->getConfiguration()->getHydrationCacheImpl(); + $profile = $profile->setResultCacheDriver($resultCacheDriver); + } + + $this->_hydrationCacheProfile = $profile; + + return $this; + } + + /** + * @return \Doctrine\DBAL\Cache\QueryCacheProfile + */ + public function getHydrationCacheProfile() + { + return $this->_hydrationCacheProfile; + } + + /** + * Set a cache profile for the result cache. + * + * If no result cache driver is set in the QueryCacheProfile, the default + * result cache driver is used from the configuration. + * + * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile + * @return \Doctrine\ORM\AbstractQuery + */ + public function setResultCacheProfile(QueryCacheProfile $profile = null) + { + if ( ! $profile->getResultCacheDriver()) { + $resultCacheDriver = $this->_em->getConfiguration()->getResultCacheImpl(); + $profile = $profile->setResultCacheDriver($resultCacheDriver); + } + + $this->_queryCacheProfile = $profile; + + return $this; + } + + /** + * Defines a cache driver to be used for caching result sets and implictly enables caching. + * + * @param \Doctrine\Common\Cache\Cache $driver Cache driver + * @return \Doctrine\ORM\AbstractQuery + */ + public function setResultCacheDriver($resultCacheDriver = null) + { + if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) { + throw ORMException::invalidResultCacheDriver(); + } + + $this->_queryCacheProfile = $this->_queryCacheProfile + ? $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver) + : new QueryCacheProfile(0, null, $resultCacheDriver); + + return $this; + } + + /** + * Returns the cache driver used for caching result sets. + * + * @deprecated + * @return \Doctrine\Common\Cache\Cache Cache driver + */ + public function getResultCacheDriver() + { + if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) { + return $this->_queryCacheProfile->getResultCacheDriver(); + } + + return $this->_em->getConfiguration()->getResultCacheImpl(); + } + + /** + * Set whether or not to cache the results of this query and if so, for + * how long and which ID to use for the cache entry. + * + * @param boolean $bool + * @param integer $lifetime + * @param string $resultCacheId + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function useResultCache($bool, $lifetime = null, $resultCacheId = null) + { + if ($bool) { + $this->setResultCacheLifetime($lifetime); + $this->setResultCacheId($resultCacheId); + + return $this; + } + + $this->_queryCacheProfile = null; + + return $this; + } + + /** + * Defines how long the result cache will be active before expire. + * + * @param integer $lifetime How long the cache entry is valid. + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setResultCacheLifetime($lifetime) + { + $lifetime = ($lifetime !== null) ? (int) $lifetime : 0; + + $this->_queryCacheProfile = $this->_queryCacheProfile + ? $this->_queryCacheProfile->setLifetime($lifetime) + : new QueryCacheProfile($lifetime, null, $this->_em->getConfiguration()->getResultCacheImpl()); + + return $this; + } + + /** + * Retrieves the lifetime of resultset cache. + * + * @deprecated + * @return integer + */ + public function getResultCacheLifetime() + { + return $this->_queryCacheProfile ? $this->_queryCacheProfile->getLifetime() : 0; + } + + /** + * Defines if the result cache is active or not. + * + * @param boolean $expire Whether or not to force resultset cache expiration. + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function expireResultCache($expire = true) + { + $this->_expireResultCache = $expire; + + return $this; + } + + /** + * Retrieves if the resultset cache is active or not. + * + * @return boolean + */ + public function getExpireResultCache() + { + return $this->_expireResultCache; + } + + /** + * @return QueryCacheProfile + */ + public function getQueryCacheProfile() + { + return $this->_queryCacheProfile; + } + + /** + * Change the default fetch mode of an association for this query. + * + * $fetchMode can be one of ClassMetadata::FETCH_EAGER or ClassMetadata::FETCH_LAZY + * + * @param string $class + * @param string $assocName + * @param int $fetchMode + * @return AbstractQuery + */ + public function setFetchMode($class, $assocName, $fetchMode) + { + if ($fetchMode !== Mapping\ClassMetadata::FETCH_EAGER) { + $fetchMode = Mapping\ClassMetadata::FETCH_LAZY; + } + + $this->_hints['fetchMode'][$class][$assocName] = $fetchMode; + + return $this; + } + + /** + * Defines the processing mode to be used during hydration / result set transformation. + * + * @param integer $hydrationMode Doctrine processing mode to be used during hydration process. + * One of the Query::HYDRATE_* constants. + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setHydrationMode($hydrationMode) + { + $this->_hydrationMode = $hydrationMode; + + return $this; + } + + /** + * Gets the hydration mode currently used by the query. + * + * @return integer + */ + public function getHydrationMode() + { + return $this->_hydrationMode; + } + + /** + * Gets the list of results for the query. + * + * Alias for execute(array(), $hydrationMode = HYDRATE_OBJECT). + * + * @return array + */ + public function getResult($hydrationMode = self::HYDRATE_OBJECT) + { + return $this->execute(array(), $hydrationMode); + } + + /** + * Gets the array of results for the query. + * + * Alias for execute(array(), HYDRATE_ARRAY). + * + * @return array + */ + public function getArrayResult() + { + return $this->execute(array(), self::HYDRATE_ARRAY); + } + + /** + * Gets the scalar results for the query. + * + * Alias for execute(array(), HYDRATE_SCALAR). + * + * @return array + */ + public function getScalarResult() + { + return $this->execute(array(), self::HYDRATE_SCALAR); + } + + /** + * Get exactly one result or null. + * + * @throws NonUniqueResultException + * @param int $hydrationMode + * @return mixed + */ + public function getOneOrNullResult($hydrationMode = null) + { + $result = $this->execute(array(), $hydrationMode); + + if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { + return null; + } + + if ( ! is_array($result)) { + return $result; + } + + if (count($result) > 1) { + throw new NonUniqueResultException; + } + + return array_shift($result); + } + + /** + * Gets the single result of the query. + * + * Enforces the presence as well as the uniqueness of the result. + * + * If the result is not unique, a NonUniqueResultException is thrown. + * If there is no result, a NoResultException is thrown. + * + * @param integer $hydrationMode + * @return mixed + * @throws NonUniqueResultException If the query result is not unique. + * @throws NoResultException If the query returned no result. + */ + public function getSingleResult($hydrationMode = null) + { + $result = $this->execute(array(), $hydrationMode); + + if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { + throw new NoResultException; + } + + if ( ! is_array($result)) { + return $result; + } + + if (count($result) > 1) { + throw new NonUniqueResultException; + } + + return array_shift($result); + } + + /** + * Gets the single scalar result of the query. + * + * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR). + * + * @return mixed + * @throws QueryException If the query result is not unique. + */ + public function getSingleScalarResult() + { + return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR); + } + + /** + * Sets a query hint. If the hint name is not recognized, it is silently ignored. + * + * @param string $name The name of the hint. + * @param mixed $value The value of the hint. + * @return \Doctrine\ORM\AbstractQuery + */ + public function setHint($name, $value) + { + $this->_hints[$name] = $value; + + return $this; + } + + /** + * Gets the value of a query hint. If the hint name is not recognized, FALSE is returned. + * + * @param string $name The name of the hint. + * @return mixed The value of the hint or FALSE, if the hint name is not recognized. + */ + public function getHint($name) + { + return isset($this->_hints[$name]) ? $this->_hints[$name] : false; + } + + /** + * Return the key value map of query hints that are currently set. + * + * @return array + */ + public function getHints() + { + return $this->_hints; + } + + /** + * Executes the query and returns an IterableResult that can be used to incrementally + * iterate over the result. + * + * @param array $params The query parameters. + * @param integer $hydrationMode The hydration mode to use. + * @return \Doctrine\ORM\Internal\Hydration\IterableResult + */ + public function iterate(array $params = array(), $hydrationMode = null) + { + if ($hydrationMode !== null) { + $this->setHydrationMode($hydrationMode); + } + + if ($params) { + $this->setParameters($params); + } + + $stmt = $this->_doExecute(); + + return $this->_em->newHydrator($this->_hydrationMode)->iterate( + $stmt, $this->_resultSetMapping, $this->_hints + ); + } + + /** + * Executes the query. + * + * @param array $params Any additional query parameters. + * @param integer $hydrationMode Processing mode to be used during the hydration process. + * @return mixed + */ + public function execute($params = array(), $hydrationMode = null) + { + if ($hydrationMode !== null) { + $this->setHydrationMode($hydrationMode); + } + + if ($params) { + $this->setParameters($params); + } + + $setCacheEntry = function() {}; + + if ($this->_hydrationCacheProfile !== null) { + list($cacheKey, $realCacheKey) = $this->getHydrationCacheId(); + + $queryCacheProfile = $this->getHydrationCacheProfile(); + $cache = $queryCacheProfile->getResultCacheDriver(); + $result = $cache->fetch($cacheKey); + + if (isset($result[$realCacheKey])) { + return $result[$realCacheKey]; + } + + if ( ! $result) { + $result = array(); + } + + $setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) { + $result[$realCacheKey] = $data; + $cache->save($cacheKey, $result, $queryCacheProfile->getLifetime()); + }; + } + + $stmt = $this->_doExecute(); + + if (is_numeric($stmt)) { + $setCacheEntry($stmt); + + return $stmt; + } + + $data = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll( + $stmt, $this->_resultSetMapping, $this->_hints + ); + + $setCacheEntry($data); + + return $data; + } + + /** + * Get the result cache id to use to store the result set cache entry. + * Will return the configured id if it exists otherwise a hash will be + * automatically generated for you. + * + * @return array ($key, $hash) + */ + protected function getHydrationCacheId() + { + $params = $this->getParameters(); + + foreach ($params AS $key => $value) { + $params[$key] = $this->processParameterValue($value); + } + + $sql = $this->getSQL(); + $queryCacheProfile = $this->getHydrationCacheProfile(); + $hints = $this->getHints(); + $hints['hydrationMode'] = $this->getHydrationMode(); + ksort($hints); + + return $queryCacheProfile->generateCacheKeys($sql, $params, $hints); + } + + /** + * Set the result cache id to use to store the result set cache entry. + * If this is not explicitely set by the developer then a hash is automatically + * generated for you. + * + * @param string $id + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setResultCacheId($id) + { + $this->_queryCacheProfile = $this->_queryCacheProfile + ? $this->_queryCacheProfile->setCacheKey($id) + : new QueryCacheProfile(0, $id, $this->_em->getConfiguration()->getResultCacheImpl()); + + return $this; + } + + /** + * Get the result cache id to use to store the result set cache entry if set. + * + * @deprecated + * @return string + */ + public function getResultCacheId() + { + return $this->_queryCacheProfile ? $this->_queryCacheProfile->getCacheKey() : null; + } + + /** + * Executes the query and returns a the resulting Statement object. + * + * @return \Doctrine\DBAL\Driver\Statement The executed database statement that holds the results. + */ + abstract protected function _doExecute(); + + /** + * Cleanup Query resource when clone is called. + * + * @return void + */ + public function __clone() + { + $this->_params = array(); + $this->_paramTypes = array(); + $this->_hints = array(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Configuration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Configuration.php new file mode 100644 index 0000000..4189f91 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Configuration.php @@ -0,0 +1,573 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Cache\Cache, + Doctrine\Common\Cache\ArrayCache, + Doctrine\Common\Annotations\AnnotationRegistry, + Doctrine\Common\Annotations\AnnotationReader, + Doctrine\ORM\Mapping\Driver\Driver, + Doctrine\ORM\Mapping\Driver\AnnotationDriver; + +/** + * Configuration container for all configuration options of Doctrine. + * It combines all configuration options from DBAL & ORM. + * + * @since 2.0 + * @internal When adding a new configuration option just write a getter/setter pair. + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Configuration extends \Doctrine\DBAL\Configuration +{ + /** + * Sets the directory where Doctrine generates any necessary proxy class files. + * + * @param string $dir + */ + public function setProxyDir($dir) + { + $this->_attributes['proxyDir'] = $dir; + } + + /** + * Gets the directory where Doctrine generates any necessary proxy class files. + * + * @return string + */ + public function getProxyDir() + { + return isset($this->_attributes['proxyDir']) ? + $this->_attributes['proxyDir'] : null; + } + + /** + * Gets a boolean flag that indicates whether proxy classes should always be regenerated + * during each script execution. + * + * @return boolean + */ + public function getAutoGenerateProxyClasses() + { + return isset($this->_attributes['autoGenerateProxyClasses']) ? + $this->_attributes['autoGenerateProxyClasses'] : true; + } + + /** + * Sets a boolean flag that indicates whether proxy classes should always be regenerated + * during each script execution. + * + * @param boolean $bool + */ + public function setAutoGenerateProxyClasses($bool) + { + $this->_attributes['autoGenerateProxyClasses'] = $bool; + } + + /** + * Gets the namespace where proxy classes reside. + * + * @return string + */ + public function getProxyNamespace() + { + return isset($this->_attributes['proxyNamespace']) ? + $this->_attributes['proxyNamespace'] : null; + } + + /** + * Sets the namespace where proxy classes reside. + * + * @param string $ns + */ + public function setProxyNamespace($ns) + { + $this->_attributes['proxyNamespace'] = $ns; + } + + /** + * Sets the cache driver implementation that is used for metadata caching. + * + * @param Driver $driverImpl + * @todo Force parameter to be a Closure to ensure lazy evaluation + * (as soon as a metadata cache is in effect, the driver never needs to initialize). + */ + public function setMetadataDriverImpl(Driver $driverImpl) + { + $this->_attributes['metadataDriverImpl'] = $driverImpl; + } + + /** + * Add a new default annotation driver with a correctly configured annotation reader. + * + * @param array $paths + * @return Mapping\Driver\AnnotationDriver + */ + public function newDefaultAnnotationDriver($paths = array()) + { + if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) { + // Register the ORM Annotations in the AnnotationRegistry + AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php'); + + $reader = new \Doctrine\Common\Annotations\SimpleAnnotationReader(); + $reader->addNamespace('Doctrine\ORM\Mapping'); + $reader = new \Doctrine\Common\Annotations\CachedReader($reader, new ArrayCache()); + } else if (version_compare(\Doctrine\Common\Version::VERSION, '2.1.0-DEV', '>=')) { + // Register the ORM Annotations in the AnnotationRegistry + AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php'); + + $reader = new AnnotationReader(); + $reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\'); + $reader->setIgnoreNotImportedAnnotations(true); + $reader->setEnableParsePhpImports(false); + $reader = new \Doctrine\Common\Annotations\CachedReader( + new \Doctrine\Common\Annotations\IndexedReader($reader), new ArrayCache() + ); + } else { + $reader = new AnnotationReader(); + $reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\'); + } + return new AnnotationDriver($reader, (array)$paths); + } + + /** + * Adds a namespace under a certain alias. + * + * @param string $alias + * @param string $namespace + */ + public function addEntityNamespace($alias, $namespace) + { + $this->_attributes['entityNamespaces'][$alias] = $namespace; + } + + /** + * Resolves a registered namespace alias to the full namespace. + * + * @param string $entityNamespaceAlias + * @return string + * @throws MappingException + */ + public function getEntityNamespace($entityNamespaceAlias) + { + if ( ! isset($this->_attributes['entityNamespaces'][$entityNamespaceAlias])) { + throw ORMException::unknownEntityNamespace($entityNamespaceAlias); + } + + return trim($this->_attributes['entityNamespaces'][$entityNamespaceAlias], '\\'); + } + + /** + * Set the entity alias map + * + * @param array $entityAliasMap + * @return void + */ + public function setEntityNamespaces(array $entityNamespaces) + { + $this->_attributes['entityNamespaces'] = $entityNamespaces; + } + + /** + * Retrieves the list of registered entity namespace aliases. + * + * @return array + */ + public function getEntityNamespaces() + { + return $this->_attributes['entityNamespaces']; + } + + /** + * Gets the cache driver implementation that is used for the mapping metadata. + * + * @throws ORMException + * @return Mapping\Driver\Driver + */ + public function getMetadataDriverImpl() + { + return isset($this->_attributes['metadataDriverImpl']) ? + $this->_attributes['metadataDriverImpl'] : null; + } + + /** + * Gets the cache driver implementation that is used for the query cache (SQL cache). + * + * @return \Doctrine\Common\Cache\Cache + */ + public function getQueryCacheImpl() + { + return isset($this->_attributes['queryCacheImpl']) ? + $this->_attributes['queryCacheImpl'] : null; + } + + /** + * Sets the cache driver implementation that is used for the query cache (SQL cache). + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + */ + public function setQueryCacheImpl(Cache $cacheImpl) + { + $this->_attributes['queryCacheImpl'] = $cacheImpl; + } + + /** + * Gets the cache driver implementation that is used for the hydration cache (SQL cache). + * + * @return \Doctrine\Common\Cache\Cache + */ + public function getHydrationCacheImpl() + { + return isset($this->_attributes['hydrationCacheImpl']) + ? $this->_attributes['hydrationCacheImpl'] + : null; + } + + /** + * Sets the cache driver implementation that is used for the hydration cache (SQL cache). + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + */ + public function setHydrationCacheImpl(Cache $cacheImpl) + { + $this->_attributes['hydrationCacheImpl'] = $cacheImpl; + } + + /** + * Gets the cache driver implementation that is used for metadata caching. + * + * @return \Doctrine\Common\Cache\Cache + */ + public function getMetadataCacheImpl() + { + return isset($this->_attributes['metadataCacheImpl']) ? + $this->_attributes['metadataCacheImpl'] : null; + } + + /** + * Sets the cache driver implementation that is used for metadata caching. + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + */ + public function setMetadataCacheImpl(Cache $cacheImpl) + { + $this->_attributes['metadataCacheImpl'] = $cacheImpl; + } + + /** + * Adds a named DQL query to the configuration. + * + * @param string $name The name of the query. + * @param string $dql The DQL query string. + */ + public function addNamedQuery($name, $dql) + { + $this->_attributes['namedQueries'][$name] = $dql; + } + + /** + * Gets a previously registered named DQL query. + * + * @param string $name The name of the query. + * @return string The DQL query. + */ + public function getNamedQuery($name) + { + if ( ! isset($this->_attributes['namedQueries'][$name])) { + throw ORMException::namedQueryNotFound($name); + } + return $this->_attributes['namedQueries'][$name]; + } + + /** + * Adds a named native query to the configuration. + * + * @param string $name The name of the query. + * @param string $sql The native SQL query string. + * @param ResultSetMapping $rsm The ResultSetMapping used for the results of the SQL query. + */ + public function addNamedNativeQuery($name, $sql, Query\ResultSetMapping $rsm) + { + $this->_attributes['namedNativeQueries'][$name] = array($sql, $rsm); + } + + /** + * Gets the components of a previously registered named native query. + * + * @param string $name The name of the query. + * @return array A tuple with the first element being the SQL string and the second + * element being the ResultSetMapping. + */ + public function getNamedNativeQuery($name) + { + if ( ! isset($this->_attributes['namedNativeQueries'][$name])) { + throw ORMException::namedNativeQueryNotFound($name); + } + return $this->_attributes['namedNativeQueries'][$name]; + } + + /** + * Ensures that this Configuration instance contains settings that are + * suitable for a production environment. + * + * @throws ORMException If a configuration setting has a value that is not + * suitable for a production environment. + */ + public function ensureProductionSettings() + { + if ( !$this->getQueryCacheImpl()) { + throw ORMException::queryCacheNotConfigured(); + } + if ( !$this->getMetadataCacheImpl()) { + throw ORMException::metadataCacheNotConfigured(); + } + if ($this->getAutoGenerateProxyClasses()) { + throw ORMException::proxyClassesAlwaysRegenerating(); + } + } + + /** + * Registers a custom DQL function that produces a string value. + * Such a function can then be used in any DQL statement in any place where string + * functions are allowed. + * + * DQL function names are case-insensitive. + * + * @param string $name + * @param string $className + */ + public function addCustomStringFunction($name, $className) + { + $this->_attributes['customStringFunctions'][strtolower($name)] = $className; + } + + /** + * Gets the implementation class name of a registered custom string DQL function. + * + * @param string $name + * @return string + */ + public function getCustomStringFunction($name) + { + $name = strtolower($name); + return isset($this->_attributes['customStringFunctions'][$name]) ? + $this->_attributes['customStringFunctions'][$name] : null; + } + + /** + * Sets a map of custom DQL string functions. + * + * Keys must be function names and values the FQCN of the implementing class. + * The function names will be case-insensitive in DQL. + * + * Any previously added string functions are discarded. + * + * @param array $functions The map of custom DQL string functions. + */ + public function setCustomStringFunctions(array $functions) + { + $this->_attributes['customStringFunctions'] = array_change_key_case($functions); + } + + /** + * Registers a custom DQL function that produces a numeric value. + * Such a function can then be used in any DQL statement in any place where numeric + * functions are allowed. + * + * DQL function names are case-insensitive. + * + * @param string $name + * @param string $className + */ + public function addCustomNumericFunction($name, $className) + { + $this->_attributes['customNumericFunctions'][strtolower($name)] = $className; + } + + /** + * Gets the implementation class name of a registered custom numeric DQL function. + * + * @param string $name + * @return string + */ + public function getCustomNumericFunction($name) + { + $name = strtolower($name); + return isset($this->_attributes['customNumericFunctions'][$name]) ? + $this->_attributes['customNumericFunctions'][$name] : null; + } + + /** + * Sets a map of custom DQL numeric functions. + * + * Keys must be function names and values the FQCN of the implementing class. + * The function names will be case-insensitive in DQL. + * + * Any previously added numeric functions are discarded. + * + * @param array $functions The map of custom DQL numeric functions. + */ + public function setCustomNumericFunctions(array $functions) + { + $this->_attributes['customNumericFunctions'] = array_change_key_case($functions); + } + + /** + * Registers a custom DQL function that produces a date/time value. + * Such a function can then be used in any DQL statement in any place where date/time + * functions are allowed. + * + * DQL function names are case-insensitive. + * + * @param string $name + * @param string $className + */ + public function addCustomDatetimeFunction($name, $className) + { + $this->_attributes['customDatetimeFunctions'][strtolower($name)] = $className; + } + + /** + * Gets the implementation class name of a registered custom date/time DQL function. + * + * @param string $name + * @return string + */ + public function getCustomDatetimeFunction($name) + { + $name = strtolower($name); + return isset($this->_attributes['customDatetimeFunctions'][$name]) ? + $this->_attributes['customDatetimeFunctions'][$name] : null; + } + + /** + * Sets a map of custom DQL date/time functions. + * + * Keys must be function names and values the FQCN of the implementing class. + * The function names will be case-insensitive in DQL. + * + * Any previously added date/time functions are discarded. + * + * @param array $functions The map of custom DQL date/time functions. + */ + public function setCustomDatetimeFunctions(array $functions) + { + $this->_attributes['customDatetimeFunctions'] = array_change_key_case($functions); + } + + /** + * Get the hydrator class for the given hydration mode name. + * + * @param string $modeName The hydration mode name. + * @return string $hydrator The hydrator class name. + */ + public function getCustomHydrationMode($modeName) + { + return isset($this->_attributes['customHydrationModes'][$modeName]) ? + $this->_attributes['customHydrationModes'][$modeName] : null; + } + + /** + * Add a custom hydration mode. + * + * @param string $modeName The hydration mode name. + * @param string $hydrator The hydrator class name. + */ + public function addCustomHydrationMode($modeName, $hydrator) + { + $this->_attributes['customHydrationModes'][$modeName] = $hydrator; + } + + /** + * Set a class metadata factory. + * + * @param string $cmf + */ + public function setClassMetadataFactoryName($cmfName) + { + $this->_attributes['classMetadataFactoryName'] = $cmfName; + } + + /** + * @return string + */ + public function getClassMetadataFactoryName() + { + if (!isset($this->_attributes['classMetadataFactoryName'])) { + $this->_attributes['classMetadataFactoryName'] = 'Doctrine\ORM\Mapping\ClassMetadataFactory'; + } + return $this->_attributes['classMetadataFactoryName']; + } + + /** + * Add a filter to the list of possible filters. + * + * @param string $name The name of the filter. + * @param string $className The class name of the filter. + */ + public function addFilter($name, $className) + { + $this->_attributes['filters'][$name] = $className; + } + + /** + * Gets the class name for a given filter name. + * + * @param string $name The name of the filter. + * + * @return string The class name of the filter, or null of it is not + * defined. + */ + public function getFilterClassName($name) + { + return isset($this->_attributes['filters'][$name]) ? + $this->_attributes['filters'][$name] : null; + } + + /** + * Set default repository class. + * + * @since 2.2 + * @param string $className + * @throws ORMException If not is a \Doctrine\ORM\EntityRepository + */ + public function setDefaultRepositoryClassName($className) + { + if ($className != "Doctrine\ORM\EntityRepository" && + !is_subclass_of($className, 'Doctrine\ORM\EntityRepository')){ + throw ORMException::invalidEntityRepository($className); + } + $this->_attributes['defaultRepositoryClassName'] = $className; + } + + /** + * Get default repository class. + * + * @since 2.2 + * @return string + */ + public function getDefaultRepositoryClassName() + { + return isset($this->_attributes['defaultRepositoryClassName']) ? + $this->_attributes['defaultRepositoryClassName'] : 'Doctrine\ORM\EntityRepository'; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php new file mode 100644 index 0000000..ab3e4c7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php @@ -0,0 +1,834 @@ +. + */ + +namespace Doctrine\ORM; + +use Closure, Exception, + Doctrine\Common\EventManager, + Doctrine\Common\Persistence\ObjectManager, + Doctrine\DBAL\Connection, + Doctrine\DBAL\LockMode, + Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\Mapping\ClassMetadataFactory, + Doctrine\ORM\Query\ResultSetMapping, + Doctrine\ORM\Proxy\ProxyFactory, + Doctrine\ORM\Query\FilterCollection; + +/** + * The EntityManager is the central access point to ORM functionality. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityManager implements ObjectManager +{ + /** + * The used Configuration. + * + * @var \Doctrine\ORM\Configuration + */ + private $config; + + /** + * The database connection used by the EntityManager. + * + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * The metadata factory, used to retrieve the ORM metadata of entity classes. + * + * @var \Doctrine\ORM\Mapping\ClassMetadataFactory + */ + private $metadataFactory; + + /** + * The EntityRepository instances. + * + * @var array + */ + private $repositories = array(); + + /** + * The UnitOfWork used to coordinate object-level transactions. + * + * @var \Doctrine\ORM\UnitOfWork + */ + private $unitOfWork; + + /** + * The event manager that is the central point of the event system. + * + * @var \Doctrine\Common\EventManager + */ + private $eventManager; + + /** + * The maintained (cached) hydrators. One instance per type. + * + * @var array + */ + private $hydrators = array(); + + /** + * The proxy factory used to create dynamic proxies. + * + * @var \Doctrine\ORM\Proxy\ProxyFactory + */ + private $proxyFactory; + + /** + * The expression builder instance used to generate query expressions. + * + * @var \Doctrine\ORM\Query\Expr + */ + private $expressionBuilder; + + /** + * Whether the EntityManager is closed or not. + * + * @var bool + */ + private $closed = false; + + /** + * Collection of query filters. + * + * @var Doctrine\ORM\Query\FilterCollection + */ + private $filterCollection; + + /** + * Creates a new EntityManager that operates on the given database connection + * and uses the given Configuration and EventManager implementations. + * + * @param \Doctrine\DBAL\Connection $conn + * @param \Doctrine\ORM\Configuration $config + * @param \Doctrine\Common\EventManager $eventManager + */ + protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager) + { + $this->conn = $conn; + $this->config = $config; + $this->eventManager = $eventManager; + + $metadataFactoryClassName = $config->getClassMetadataFactoryName(); + $this->metadataFactory = new $metadataFactoryClassName; + $this->metadataFactory->setEntityManager($this); + $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl()); + + $this->unitOfWork = new UnitOfWork($this); + $this->proxyFactory = new ProxyFactory( + $this, + $config->getProxyDir(), + $config->getProxyNamespace(), + $config->getAutoGenerateProxyClasses() + ); + } + + /** + * Gets the database connection object used by the EntityManager. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->conn; + } + + /** + * Gets the metadata factory used to gather the metadata of classes. + * + * @return \Doctrine\ORM\Mapping\ClassMetadataFactory + */ + public function getMetadataFactory() + { + return $this->metadataFactory; + } + + /** + * Gets an ExpressionBuilder used for object-oriented construction of query expressions. + * + * Example: + * + * + * $qb = $em->createQueryBuilder(); + * $expr = $em->getExpressionBuilder(); + * $qb->select('u')->from('User', 'u') + * ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2))); + * + * + * @return \Doctrine\ORM\Query\Expr + */ + public function getExpressionBuilder() + { + if ($this->expressionBuilder === null) { + $this->expressionBuilder = new Query\Expr; + } + + return $this->expressionBuilder; + } + + /** + * Starts a transaction on the underlying database connection. + * + * @deprecated Use {@link getConnection}.beginTransaction(). + */ + public function beginTransaction() + { + $this->conn->beginTransaction(); + } + + /** + * Executes a function in a transaction. + * + * The function gets passed this EntityManager instance as an (optional) parameter. + * + * {@link flush} is invoked prior to transaction commit. + * + * If an exception occurs during execution of the function or flushing or transaction commit, + * the transaction is rolled back, the EntityManager closed and the exception re-thrown. + * + * @param Closure $func The function to execute transactionally. + * @return mixed Returns the non-empty value returned from the closure or true instead + */ + public function transactional(Closure $func) + { + $this->conn->beginTransaction(); + + try { + $return = $func($this); + + $this->flush(); + $this->conn->commit(); + + return $return ?: true; + } catch (Exception $e) { + $this->close(); + $this->conn->rollback(); + + throw $e; + } + } + + /** + * Commits a transaction on the underlying database connection. + * + * @deprecated Use {@link getConnection}.commit(). + */ + public function commit() + { + $this->conn->commit(); + } + + /** + * Performs a rollback on the underlying database connection. + * + * @deprecated Use {@link getConnection}.rollback(). + */ + public function rollback() + { + $this->conn->rollback(); + } + + /** + * Returns the ORM metadata descriptor for a class. + * + * The class name must be the fully-qualified class name without a leading backslash + * (as it is returned by get_class($obj)) or an aliased class name. + * + * Examples: + * MyProject\Domain\User + * sales:PriceRequest + * + * @return \Doctrine\ORM\Mapping\ClassMetadata + * @internal Performance-sensitive method. + */ + public function getClassMetadata($className) + { + return $this->metadataFactory->getMetadataFor($className); + } + + /** + * Creates a new Query object. + * + * @param string The DQL string. + * @return \Doctrine\ORM\Query + */ + public function createQuery($dql = "") + { + $query = new Query($this); + + if ( ! empty($dql)) { + $query->setDql($dql); + } + + return $query; + } + + /** + * Creates a Query from a named query. + * + * @param string $name + * @return \Doctrine\ORM\Query + */ + public function createNamedQuery($name) + { + return $this->createQuery($this->config->getNamedQuery($name)); + } + + /** + * Creates a native SQL query. + * + * @param string $sql + * @param ResultSetMapping $rsm The ResultSetMapping to use. + * @return NativeQuery + */ + public function createNativeQuery($sql, ResultSetMapping $rsm) + { + $query = new NativeQuery($this); + $query->setSql($sql); + $query->setResultSetMapping($rsm); + + return $query; + } + + /** + * Creates a NativeQuery from a named native query. + * + * @param string $name + * @return \Doctrine\ORM\NativeQuery + */ + public function createNamedNativeQuery($name) + { + list($sql, $rsm) = $this->config->getNamedNativeQuery($name); + + return $this->createNativeQuery($sql, $rsm); + } + + /** + * Create a QueryBuilder instance + * + * @return QueryBuilder $qb + */ + public function createQueryBuilder() + { + return new QueryBuilder($this); + } + + /** + * Flushes all changes to objects that have been queued up to now to the database. + * This effectively synchronizes the in-memory state of managed objects with the + * database. + * + * If an entity is explicitly passed to this method only this entity and + * the cascade-persist semantics + scheduled inserts/removals are synchronized. + * + * @param object $entity + * @throws \Doctrine\ORM\OptimisticLockException If a version check on an entity that + * makes use of optimistic locking fails. + */ + public function flush($entity = null) + { + $this->errorIfClosed(); + + $this->unitOfWork->commit($entity); + } + + /** + * Finds an Entity by its identifier. + * + * This is just a convenient shortcut for getRepository($entityName)->find($id). + * + * @param string $entityName + * @param mixed $identifier + * @param int $lockMode + * @param int $lockVersion + * @return object + */ + public function find($entityName, $identifier, $lockMode = LockMode::NONE, $lockVersion = null) + { + return $this->getRepository($entityName)->find($identifier, $lockMode, $lockVersion); + } + + /** + * Gets a reference to the entity identified by the given type and identifier + * without actually loading it, if the entity is not yet loaded. + * + * @param string $entityName The name of the entity type. + * @param mixed $id The entity identifier. + * @return object The entity reference. + */ + public function getReference($entityName, $id) + { + $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); + if ( ! is_array($id)) { + $id = array($class->identifier[0] => $id); + } + $sortedId = array(); + foreach ($class->identifier as $identifier) { + if (!isset($id[$identifier])) { + throw ORMException::missingIdentifierField($class->name, $identifier); + } + $sortedId[$identifier] = $id[$identifier]; + } + + // Check identity map first, if its already in there just return it. + if ($entity = $this->unitOfWork->tryGetById($sortedId, $class->rootEntityName)) { + return ($entity instanceof $class->name) ? $entity : null; + } + + if ($class->subClasses) { + return $this->find($entityName, $sortedId); + } + + if ( ! is_array($sortedId)) { + $sortedId = array($class->identifier[0] => $sortedId); + } + + $entity = $this->proxyFactory->getProxy($class->name, $sortedId); + $this->unitOfWork->registerManaged($entity, $sortedId, array()); + + return $entity; + } + + /** + * Gets a partial reference to the entity identified by the given type and identifier + * without actually loading it, if the entity is not yet loaded. + * + * The returned reference may be a partial object if the entity is not yet loaded/managed. + * If it is a partial object it will not initialize the rest of the entity state on access. + * Thus you can only ever safely access the identifier of an entity obtained through + * this method. + * + * The use-cases for partial references involve maintaining bidirectional associations + * without loading one side of the association or to update an entity without loading it. + * Note, however, that in the latter case the original (persistent) entity data will + * never be visible to the application (especially not event listeners) as it will + * never be loaded in the first place. + * + * @param string $entityName The name of the entity type. + * @param mixed $identifier The entity identifier. + * @return object The (partial) entity reference. + */ + public function getPartialReference($entityName, $identifier) + { + $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); + + // Check identity map first, if its already in there just return it. + if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) { + return ($entity instanceof $class->name) ? $entity : null; + } + + if ( ! is_array($identifier)) { + $identifier = array($class->identifier[0] => $identifier); + } + + $entity = $class->newInstance(); + $class->setIdentifierValues($entity, $identifier); + $this->unitOfWork->registerManaged($entity, $identifier, array()); + $this->unitOfWork->markReadOnly($entity); + + return $entity; + } + + /** + * Clears the EntityManager. All entities that are currently managed + * by this EntityManager become detached. + * + * @param string $entityName if given, only entities of this type will get detached + */ + public function clear($entityName = null) + { + $this->unitOfWork->clear($entityName); + } + + /** + * Closes the EntityManager. All entities that are currently managed + * by this EntityManager become detached. The EntityManager may no longer + * be used after it is closed. + */ + public function close() + { + $this->clear(); + $this->closed = true; + } + + /** + * Tells the EntityManager to make an instance managed and persistent. + * + * The entity will be entered into the database at or before transaction + * commit or as a result of the flush operation. + * + * NOTE: The persist operation always considers entities that are not yet known to + * this EntityManager as NEW. Do not pass detached entities to the persist operation. + * + * @param object $object The instance to make managed and persistent. + */ + public function persist($entity) + { + if ( ! is_object($entity)) { + throw new \InvalidArgumentException(gettype($entity)); + } + + $this->errorIfClosed(); + + $this->unitOfWork->persist($entity); + } + + /** + * Removes an entity instance. + * + * A removed entity will be removed from the database at or before transaction commit + * or as a result of the flush operation. + * + * @param object $entity The entity instance to remove. + */ + public function remove($entity) + { + if ( ! is_object($entity)) { + throw new \InvalidArgumentException(gettype($entity)); + } + + $this->errorIfClosed(); + + $this->unitOfWork->remove($entity); + } + + /** + * Refreshes the persistent state of an entity from the database, + * overriding any local changes that have not yet been persisted. + * + * @param object $entity The entity to refresh. + */ + public function refresh($entity) + { + if ( ! is_object($entity)) { + throw new \InvalidArgumentException(gettype($entity)); + } + + $this->errorIfClosed(); + + $this->unitOfWork->refresh($entity); + } + + /** + * Detaches an entity from the EntityManager, causing a managed entity to + * become detached. Unflushed changes made to the entity if any + * (including removal of the entity), will not be synchronized to the database. + * Entities which previously referenced the detached entity will continue to + * reference it. + * + * @param object $entity The entity to detach. + */ + public function detach($entity) + { + if ( ! is_object($entity)) { + throw new \InvalidArgumentException(gettype($entity)); + } + + $this->unitOfWork->detach($entity); + } + + /** + * Merges the state of a detached entity into the persistence context + * of this EntityManager and returns the managed copy of the entity. + * The entity passed to merge will not become associated/managed with this EntityManager. + * + * @param object $entity The detached entity to merge into the persistence context. + * @return object The managed copy of the entity. + */ + public function merge($entity) + { + if ( ! is_object($entity)) { + throw new \InvalidArgumentException(gettype($entity)); + } + + $this->errorIfClosed(); + + return $this->unitOfWork->merge($entity); + } + + /** + * Creates a copy of the given entity. Can create a shallow or a deep copy. + * + * @param object $entity The entity to copy. + * @return object The new entity. + * @todo Implementation need. This is necessary since $e2 = clone $e1; throws an E_FATAL when access anything on $e: + * Fatal error: Maximum function nesting level of '100' reached, aborting! + */ + public function copy($entity, $deep = false) + { + throw new \BadMethodCallException("Not implemented."); + } + + /** + * Acquire a lock on the given entity. + * + * @param object $entity + * @param int $lockMode + * @param int $lockVersion + * @throws OptimisticLockException + * @throws PessimisticLockException + */ + public function lock($entity, $lockMode, $lockVersion = null) + { + $this->unitOfWork->lock($entity, $lockMode, $lockVersion); + } + + /** + * Gets the repository for an entity class. + * + * @param string $entityName The name of the entity. + * @return EntityRepository The repository class. + */ + public function getRepository($entityName) + { + $entityName = ltrim($entityName, '\\'); + + if (isset($this->repositories[$entityName])) { + return $this->repositories[$entityName]; + } + + $metadata = $this->getClassMetadata($entityName); + $repositoryClassName = $metadata->customRepositoryClassName; + + if ($repositoryClassName === null) { + $repositoryClassName = $this->config->getDefaultRepositoryClassName(); + } + + $repository = new $repositoryClassName($this, $metadata); + + $this->repositories[$entityName] = $repository; + + return $repository; + } + + /** + * Determines whether an entity instance is managed in this EntityManager. + * + * @param object $entity + * @return boolean TRUE if this EntityManager currently manages the given entity, FALSE otherwise. + */ + public function contains($entity) + { + return $this->unitOfWork->isScheduledForInsert($entity) + || $this->unitOfWork->isInIdentityMap($entity) + && ! $this->unitOfWork->isScheduledForDelete($entity); + } + + /** + * Gets the EventManager used by the EntityManager. + * + * @return \Doctrine\Common\EventManager + */ + public function getEventManager() + { + return $this->eventManager; + } + + /** + * Gets the Configuration used by the EntityManager. + * + * @return \Doctrine\ORM\Configuration + */ + public function getConfiguration() + { + return $this->config; + } + + /** + * Throws an exception if the EntityManager is closed or currently not active. + * + * @throws ORMException If the EntityManager is closed. + */ + private function errorIfClosed() + { + if ($this->closed) { + throw ORMException::entityManagerClosed(); + } + } + + /** + * Check if the Entity manager is open or closed. + * + * @return bool + */ + public function isOpen() + { + return (!$this->closed); + } + + /** + * Gets the UnitOfWork used by the EntityManager to coordinate operations. + * + * @return \Doctrine\ORM\UnitOfWork + */ + public function getUnitOfWork() + { + return $this->unitOfWork; + } + + /** + * Gets a hydrator for the given hydration mode. + * + * This method caches the hydrator instances which is used for all queries that don't + * selectively iterate over the result. + * + * @param int $hydrationMode + * @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator + */ + public function getHydrator($hydrationMode) + { + if ( ! isset($this->hydrators[$hydrationMode])) { + $this->hydrators[$hydrationMode] = $this->newHydrator($hydrationMode); + } + + return $this->hydrators[$hydrationMode]; + } + + /** + * Create a new instance for the given hydration mode. + * + * @param int $hydrationMode + * @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator + */ + public function newHydrator($hydrationMode) + { + switch ($hydrationMode) { + case Query::HYDRATE_OBJECT: + return new Internal\Hydration\ObjectHydrator($this); + + case Query::HYDRATE_ARRAY: + return new Internal\Hydration\ArrayHydrator($this); + + case Query::HYDRATE_SCALAR: + return new Internal\Hydration\ScalarHydrator($this); + + case Query::HYDRATE_SINGLE_SCALAR: + return new Internal\Hydration\SingleScalarHydrator($this); + + case Query::HYDRATE_SIMPLEOBJECT: + return new Internal\Hydration\SimpleObjectHydrator($this); + + default: + if ($class = $this->config->getCustomHydrationMode($hydrationMode)) { + return new $class($this); + } + } + + throw ORMException::invalidHydrationMode($hydrationMode); + } + + /** + * Gets the proxy factory used by the EntityManager to create entity proxies. + * + * @return ProxyFactory + */ + public function getProxyFactory() + { + return $this->proxyFactory; + } + + /** + * Helper method to initialize a lazy loading proxy or persistent collection. + * + * This method is a no-op for other objects + * + * @param object $obj + */ + public function initializeObject($obj) + { + $this->unitOfWork->initializeObject($obj); + } + + /** + * Factory method to create EntityManager instances. + * + * @param mixed $conn An array with the connection parameters or an existing + * Connection instance. + * @param Configuration $config The Configuration instance to use. + * @param EventManager $eventManager The EventManager instance to use. + * @return EntityManager The created EntityManager. + */ + public static function create($conn, Configuration $config, EventManager $eventManager = null) + { + if ( ! $config->getMetadataDriverImpl()) { + throw ORMException::missingMappingDriverImpl(); + } + + switch (true) { + case (is_array($conn)): + $conn = \Doctrine\DBAL\DriverManager::getConnection( + $conn, $config, ($eventManager ?: new EventManager()) + ); + break; + + case ($conn instanceof Connection): + if ($eventManager !== null && $conn->getEventManager() !== $eventManager) { + throw ORMException::mismatchedEventManager(); + } + break; + + default: + throw new \InvalidArgumentException("Invalid argument: " . $conn); + } + + return new EntityManager($conn, $config, $conn->getEventManager()); + } + + /** + * Gets the enabled filters. + * + * @return FilterCollection The active filter collection. + */ + public function getFilters() + { + if (null === $this->filterCollection) { + $this->filterCollection = new FilterCollection($this); + } + + return $this->filterCollection; + } + + /** + * Checks whether the state of the filter collection is clean. + * + * @return boolean True, if the filter collection is clean. + */ + public function isFiltersStateClean() + { + return null === $this->filterCollection + || $this->filterCollection->isClean(); + } + + /** + * Checks whether the Entity Manager has filters. + * + * @return True, if the EM has a filter collection. + */ + public function hasFilters() + { + return null !== $this->filterCollection; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/EntityNotFoundException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityNotFoundException.php new file mode 100644 index 0000000..2e58132 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityNotFoundException.php @@ -0,0 +1,34 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception thrown when a Proxy fails to retrieve an Entity result. + * + * @author robo + * @since 2.0 + */ +class EntityNotFoundException extends ORMException +{ + public function __construct() + { + parent::__construct('Entity was not found.'); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php new file mode 100644 index 0000000..17cc298 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php @@ -0,0 +1,265 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\DBAL\LockMode; +use Doctrine\Common\Persistence\ObjectRepository; + +/** + * An EntityRepository serves as a repository for entities with generic as well as + * business specific methods for retrieving entities. + * + * This class is designed for inheritance and users can subclass this class to + * write their own repositories with business-specific methods to locate entities. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityRepository implements ObjectRepository +{ + /** + * @var string + */ + protected $_entityName; + + /** + * @var EntityManager + */ + protected $_em; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + protected $_class; + + /** + * Initializes a new EntityRepository. + * + * @param EntityManager $em The EntityManager to use. + * @param ClassMetadata $classMetadata The class descriptor. + */ + public function __construct($em, Mapping\ClassMetadata $class) + { + $this->_entityName = $class->name; + $this->_em = $em; + $this->_class = $class; + } + + /** + * Create a new QueryBuilder instance that is prepopulated for this entity name + * + * @param string $alias + * @return QueryBuilder $qb + */ + public function createQueryBuilder($alias) + { + return $this->_em->createQueryBuilder() + ->select($alias) + ->from($this->_entityName, $alias); + } + + /** + * Create a new Query instance based on a predefined metadata named query. + * + * @param string $queryName + * @return Query + */ + public function createNamedQuery($queryName) + { + return $this->_em->createQuery($this->_class->getNamedQuery($queryName)); + } + + /** + * Clears the repository, causing all managed entities to become detached. + */ + public function clear() + { + $this->_em->clear($this->_class->rootEntityName); + } + + /** + * Finds an entity by its primary key / identifier. + * + * @param $id The identifier. + * @param int $lockMode + * @param int $lockVersion + * @return object The entity. + */ + public function find($id, $lockMode = LockMode::NONE, $lockVersion = null) + { + if ( ! is_array($id)) { + $id = array($this->_class->identifier[0] => $id); + } + $sortedId = array(); + foreach ($this->_class->identifier as $identifier) { + if (!isset($id[$identifier])) { + throw ORMException::missingIdentifierField($this->_class->name, $identifier); + } + $sortedId[$identifier] = $id[$identifier]; + } + + // Check identity map first + if ($entity = $this->_em->getUnitOfWork()->tryGetById($sortedId, $this->_class->rootEntityName)) { + if ( ! ($entity instanceof $this->_class->name)) { + return null; + } + + if ($lockMode !== LockMode::NONE) { + $this->_em->lock($entity, $lockMode, $lockVersion); + } + + return $entity; // Hit! + } + + switch ($lockMode) { + case LockMode::NONE: + return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($sortedId); + + case LockMode::OPTIMISTIC: + if ( ! $this->_class->isVersioned) { + throw OptimisticLockException::notVersioned($this->_entityName); + } + + $entity = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($sortedId); + + $this->_em->getUnitOfWork()->lock($entity, $lockMode, $lockVersion); + + return $entity; + + default: + if ( ! $this->_em->getConnection()->isTransactionActive()) { + throw TransactionRequiredException::transactionRequired(); + } + + return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($sortedId, null, null, array(), $lockMode); + } + } + + /** + * Finds all entities in the repository. + * + * @return array The entities. + */ + public function findAll() + { + return $this->findBy(array()); + } + + /** + * Finds entities by a set of criteria. + * + * @param array $criteria + * @param array|null $orderBy + * @param int|null $limit + * @param int|null $offset + * @return array The objects. + */ + public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + { + return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->loadAll($criteria, $orderBy, $limit, $offset); + } + + /** + * Finds a single entity by a set of criteria. + * + * @param array $criteria + * @return object + */ + public function findOneBy(array $criteria) + { + return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($criteria, null, null, array(), 0, 1); + } + + /** + * Adds support for magic finders. + * + * @return array|object The found entity/entities. + * @throws BadMethodCallException If the method called is an invalid find* method + * or no find* method at all and therefore an invalid + * method call. + */ + public function __call($method, $arguments) + { + switch (true) { + case (substr($method, 0, 6) == 'findBy'): + $by = substr($method, 6, strlen($method)); + $method = 'findBy'; + break; + + case (substr($method, 0, 9) == 'findOneBy'): + $by = substr($method, 9, strlen($method)); + $method = 'findOneBy'; + break; + + default: + throw new \BadMethodCallException( + "Undefined method '$method'. The method name must start with ". + "either findBy or findOneBy!" + ); + } + + if (empty($arguments)) { + throw ORMException::findByRequiresParameter($method . $by); + } + + $fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by)); + + if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) { + return $this->$method(array($fieldName => $arguments[0])); + } + + throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by); + } + + /** + * @return string + */ + protected function getEntityName() + { + return $this->_entityName; + } + + /** + * @return string + */ + public function getClassName() + { + return $this->getEntityName(); + } + + /** + * @return EntityManager + */ + protected function getEntityManager() + { + return $this->_em; + } + + /** + * @return Mapping\ClassMetadata + */ + protected function getClassMetadata() + { + return $this->_class; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LifecycleEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LifecycleEventArgs.php new file mode 100644 index 0000000..6740a3e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LifecycleEventArgs.php @@ -0,0 +1,77 @@ +. +*/ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\EntityManager; + +/** + * Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions + * of entities. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LifecycleEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * @var object + */ + private $entity; + + /** + * Constructor + * + * @param object $entity + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct($entity, EntityManager $em) + { + $this->entity = $entity; + $this->em = $em; + } + + /** + * Retireve associated Entity. + * + * @return object + */ + public function getEntity() + { + return $this->entity; + } + + /** + * Retrieve associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php new file mode 100644 index 0000000..901bf3a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\EntityManager; + +/** + * Class that holds event arguments for a loadMetadata event. + * + * @author Jonathan H. Wage + * @since 2.0 + */ +class LoadClassMetadataEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + private $classMetadata; + + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * Constructor. + * + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $classMetadata + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(ClassMetadataInfo $classMetadata, EntityManager $em) + { + $this->classMetadata = $classMetadata; + $this->em = $em; + } + + /** + * Retrieve associated ClassMetadata. + * + * @return \Doctrine\ORM\Mapping\ClassMetadataInfo + */ + public function getClassMetadata() + { + return $this->classMetadata; + } + + /** + * Retrieve associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClearEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClearEventArgs.php new file mode 100644 index 0000000..542ac45 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClearEventArgs.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\ORM\Event; + +/** + * Provides event arguments for the onClear event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class OnClearEventArgs extends \Doctrine\Common\EventArgs +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * @var string + */ + private $entityClass; + + /** + * Constructor. + * + * @param \Doctrine\ORM\EntityManager $em + * @param string $entityClass Optional entity class + */ + public function __construct($em, $entityClass = null) + { + $this->em = $em; + $this->entityClass = $entityClass; + } + + /** + * Retrieve associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } + + /** + * Name of the entity class that is cleared, or empty if all are cleared. + * + * @return string + */ + public function getEntityClass() + { + return $this->entityClass; + } + + /** + * Check if event clears all entities. + * + * @return bool + */ + public function clearsAllEntities() + { + return ($this->entityClass === null); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnFlushEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnFlushEventArgs.php new file mode 100644 index 0000000..c4aeb2a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnFlushEventArgs.php @@ -0,0 +1,86 @@ +. +*/ + +namespace Doctrine\ORM\Event; + +use Doctrine\ORM\EntityManager; + +/** + * Provides event arguments for the preFlush event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class OnFlushEventArgs extends \Doctrine\Common\EventArgs +{ + /** + * @var Doctirne\ORM\EntityManager + */ + private $em; + + //private $entitiesToPersist = array(); + //private $entitiesToRemove = array(); + + /** + * Constructor. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + } + + /** + * Retrieve associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } + + /* + public function addEntityToPersist($entity) + { + + } + + public function addEntityToRemove($entity) + { + + } + + public function addEntityToUpdate($entity) + { + + } + + public function getEntitiesToPersist() + { + return $this->_entitiesToPersist; + } + */ +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PostFlushEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PostFlushEventArgs.php new file mode 100644 index 0000000..f45030d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PostFlushEventArgs.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\ORM\EntityManager; +use Doctrine\Common\EventArgs; + +/** + * Provides event arguments for the postFlush event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Daniel Freudenberger + */ +class PostFlushEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * Constructor. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + } + + /** + * Retrieve associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreFlushEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreFlushEventArgs.php new file mode 100644 index 0000000..b86967a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreFlushEventArgs.php @@ -0,0 +1,53 @@ +. +*/ + +namespace Doctrine\ORM\Event; + +/** + * Provides event arguments for the preFlush event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @version $Revision$ + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class PreFlushEventArgs extends \Doctrine\Common\EventArgs +{ + /** + * @var EntityManager + */ + private $_em; + + public function __construct($em) + { + $this->_em = $em; + } + + /** + * @return EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php new file mode 100644 index 0000000..0bebba1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php @@ -0,0 +1,131 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs, + Doctrine\ORM\EntityManager; + +/** + * Class that holds event arguments for a preInsert/preUpdate event. + * + * @author Guilherme Blanco + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +class PreUpdateEventArgs extends LifecycleEventArgs +{ + /** + * @var array + */ + private $entityChangeSet; + + /** + * Constructor. + * + * @param object $entity + * @param \Doctrine\ORM\EntityManager $em + * @param array $changeSet + */ + public function __construct($entity, EntityManager $em, array &$changeSet) + { + parent::__construct($entity, $em); + + $this->entityChangeSet = &$changeSet; + } + + /** + * Retrieve entity changeset. + * + * @return array + */ + public function getEntityChangeSet() + { + return $this->entityChangeSet; + } + + /** + * Check if field has a changeset. + * + * @return boolean + */ + public function hasChangedField($field) + { + return isset($this->entityChangeSet[$field]); + } + + /** + * Get the old value of the changeset of the changed field. + * + * @param string $field + * @return mixed + */ + public function getOldValue($field) + { + $this->assertValidField($field); + + return $this->entityChangeSet[$field][0]; + } + + /** + * Get the new value of the changeset of the changed field. + * + * @param string $field + * @return mixed + */ + public function getNewValue($field) + { + $this->assertValidField($field); + + return $this->entityChangeSet[$field][1]; + } + + /** + * Set the new value of this field. + * + * @param string $field + * @param mixed $value + */ + public function setNewValue($field, $value) + { + $this->assertValidField($field); + + $this->entityChangeSet[$field][1] = $value; + } + + /** + * Assert the field exists in changeset. + * + * @param string $field + */ + private function assertValidField($field) + { + if ( ! isset($this->entityChangeSet[$field])) { + throw new \InvalidArgumentException(sprintf( + 'Field "%s" is not a valid field of the entity "%s" in PreUpdateEventArgs.', + $field, + get_class($this->getEntity()) + )); + } + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Events.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Events.php new file mode 100644 index 0000000..45424e7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Events.php @@ -0,0 +1,148 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Container for all ORM events. + * + * This class cannot be instantiated. + * + * @author Roman Borschel + * @since 2.0 + */ +final class Events +{ + private function __construct() {} + /** + * The preRemove event occurs for a given entity before the respective + * EntityManager remove operation for that entity is executed. + * + * This is an entity lifecycle event. + * + * @var string + */ + const preRemove = 'preRemove'; + /** + * The postRemove event occurs for an entity after the entity has + * been deleted. It will be invoked after the database delete operations. + * + * This is an entity lifecycle event. + * + * @var string + */ + const postRemove = 'postRemove'; + /** + * The prePersist event occurs for a given entity before the respective + * EntityManager persist operation for that entity is executed. + * + * This is an entity lifecycle event. + * + * @var string + */ + const prePersist = 'prePersist'; + /** + * The postPersist event occurs for an entity after the entity has + * been made persistent. It will be invoked after the database insert operations. + * Generated primary key values are available in the postPersist event. + * + * This is an entity lifecycle event. + * + * @var string + */ + const postPersist = 'postPersist'; + /** + * The preUpdate event occurs before the database update operations to + * entity data. + * + * This is an entity lifecycle event. + * + * @var string + */ + const preUpdate = 'preUpdate'; + /** + * The postUpdate event occurs after the database update operations to + * entity data. + * + * This is an entity lifecycle event. + * + * @var string + */ + const postUpdate = 'postUpdate'; + /** + * The postLoad event occurs for an entity after the entity has been loaded + * into the current EntityManager from the database or after the refresh operation + * has been applied to it. + * + * Note that the postLoad event occurs for an entity before any associations have been + * initialized. Therefore it is not safe to access associations in a postLoad callback + * or event handler. + * + * This is an entity lifecycle event. + * + * @var string + */ + const postLoad = 'postLoad'; + /** + * The loadClassMetadata event occurs after the mapping metadata for a class + * has been loaded from a mapping source (annotations/xml/yaml). + * + * @var string + */ + const loadClassMetadata = 'loadClassMetadata'; + + /** + * The preFlush event occurs when the EntityManager#flush() operation is invoked, + * but before any changes to managed entites have been calculated. This event is + * always raised right after EntityManager#flush() call. + */ + const preFlush = 'preFlush'; + + /** + * The onFlush event occurs when the EntityManager#flush() operation is invoked, + * after any changes to managed entities have been determined but before any + * actual database operations are executed. The event is only raised if there is + * actually something to do for the underlying UnitOfWork. If nothing needs to be done, + * the onFlush event is not raised. + * + * @var string + */ + const onFlush = 'onFlush'; + + /** + * The postFlush event occurs when the EntityManager#flush() operation is invoked and + * after all actual database operations are executed successfully. The event is only raised if there is + * actually something to do for the underlying UnitOfWork. If nothing needs to be done, + * the postFlush event is not raised. The event won't be raised if an error occurs during the + * flush operation. + * + * @var string + */ + const postFlush = 'postFlush'; + + /** + * The onClear event occurs when the EntityManager#clear() operation is invoked, + * after all references to entities have been removed from the unit of work. + * + * @var string + */ + const onClear = 'onClear'; +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AbstractIdGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AbstractIdGenerator.php new file mode 100644 index 0000000..d27c00b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AbstractIdGenerator.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +abstract class AbstractIdGenerator +{ + /** + * Generates an identifier for an entity. + * + * @param \Doctrine\ORM\Entity $entity + * @return mixed + */ + abstract public function generate(EntityManager $em, $entity); + + /** + * Gets whether this generator is a post-insert generator which means that + * {@link generate()} must be called after the entity has been inserted + * into the database. + * + * By default, this method returns FALSE. Generators that have this requirement + * must override this method and return TRUE. + * + * @return boolean + */ + public function isPostInsertGenerator() + { + return false; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AssignedGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AssignedGenerator.php new file mode 100644 index 0000000..c9f9ada --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AssignedGenerator.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\ORMException; + +/** + * Special generator for application-assigned identifiers (doesnt really generate anything). + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class AssignedGenerator extends AbstractIdGenerator +{ + /** + * Returns the identifier assigned to the given entity. + * + * @param object $entity + * @return mixed + * @override + */ + public function generate(EntityManager $em, $entity) + { + $class = $em->getClassMetadata(get_class($entity)); + $idFields = $class->getIdentifierFieldNames(); + $identifier = array(); + + foreach ($idFields as $idField) { + $value = $class->reflFields[$idField]->getValue($entity); + + if ( ! isset($value)) { + throw ORMException::entityMissingAssignedIdForField($entity, $idField); + } + + if (isset($class->associationMappings[$idField])) { + if ( ! $em->getUnitOfWork()->isInIdentityMap($value)) { + throw ORMException::entityMissingForeignAssignedId($entity, $value); + } + + // NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced. + $value = current($em->getUnitOfWork()->getEntityIdentifier($value)); + } + + $identifier[$idField] = $value; + } + + return $identifier; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/IdentityGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/IdentityGenerator.php new file mode 100644 index 0000000..d244871 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/IdentityGenerator.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +/** + * Id generator that obtains IDs from special "identity" columns. These are columns + * that automatically get a database-generated, auto-incremented identifier on INSERT. + * This generator obtains the last insert id after such an insert. + */ +class IdentityGenerator extends AbstractIdGenerator +{ + /** @var string The name of the sequence to pass to lastInsertId(), if any. */ + private $_seqName; + + /** + * @param string $seqName The name of the sequence to pass to lastInsertId() + * to obtain the last generated identifier within the current + * database session/connection, if any. + */ + public function __construct($seqName = null) + { + $this->_seqName = $seqName; + } + + /** + * {@inheritdoc} + */ + public function generate(EntityManager $em, $entity) + { + return (int)$em->getConnection()->lastInsertId($this->_seqName); + } + + /** + * {@inheritdoc} + */ + public function isPostInsertGenerator() + { + return true; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/SequenceGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/SequenceGenerator.php new file mode 100644 index 0000000..7924061 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/SequenceGenerator.php @@ -0,0 +1,106 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Serializable, Doctrine\ORM\EntityManager; + +/** + * Represents an ID generator that uses a database sequence. + * + * @since 2.0 + * @author Roman Borschel + */ +class SequenceGenerator extends AbstractIdGenerator implements Serializable +{ + private $_allocationSize; + private $_sequenceName; + private $_nextValue = 0; + private $_maxValue = null; + + /** + * Initializes a new sequence generator. + * + * @param \Doctrine\ORM\EntityManager $em The EntityManager to use. + * @param string $sequenceName The name of the sequence. + * @param integer $allocationSize The allocation size of the sequence. + */ + public function __construct($sequenceName, $allocationSize) + { + $this->_sequenceName = $sequenceName; + $this->_allocationSize = $allocationSize; + } + + /** + * Generates an ID for the given entity. + * + * @param object $entity + * @return integer|float The generated value. + * @override + */ + public function generate(EntityManager $em, $entity) + { + if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) { + // Allocate new values + $conn = $em->getConnection(); + $sql = $conn->getDatabasePlatform()->getSequenceNextValSQL($this->_sequenceName); + + $this->_nextValue = (int)$conn->fetchColumn($sql); + $this->_maxValue = $this->_nextValue + $this->_allocationSize; + } + + return $this->_nextValue++; + } + + /** + * Gets the maximum value of the currently allocated bag of values. + * + * @return integer|float + */ + public function getCurrentMaxValue() + { + return $this->_maxValue; + } + + /** + * Gets the next value that will be returned by generate(). + * + * @return integer|float + */ + public function getNextValue() + { + return $this->_nextValue; + } + + public function serialize() + { + return serialize(array( + 'allocationSize' => $this->_allocationSize, + 'sequenceName' => $this->_sequenceName + )); + } + + public function unserialize($serialized) + { + $array = unserialize($serialized); + + $this->_sequenceName = $array['sequenceName']; + $this->_allocationSize = $array['allocationSize']; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/TableGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/TableGenerator.php new file mode 100644 index 0000000..24b1d90 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/TableGenerator.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +/** + * Id generator that uses a single-row database table and a hi/lo algorithm. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class TableGenerator extends AbstractIdGenerator +{ + private $_tableName; + private $_sequenceName; + private $_allocationSize; + private $_nextValue; + private $_maxValue; + + public function __construct($tableName, $sequenceName = 'default', $allocationSize = 10) + { + $this->_tableName = $tableName; + $this->_sequenceName = $sequenceName; + $this->_allocationSize = $allocationSize; + } + + public function generate(EntityManager $em, $entity) + { + if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) { + // Allocate new values + $conn = $em->getConnection(); + + if ($conn->getTransactionNestingLevel() === 0) { + // use select for update + $sql = $conn->getDatabasePlatform()->getTableHiLoCurrentValSql($this->_tableName, $this->_sequenceName); + $currentLevel = $conn->fetchColumn($sql); + + if ($currentLevel != null) { + $this->_nextValue = $currentLevel; + $this->_maxValue = $this->_nextValue + $this->_allocationSize; + + $updateSql = $conn->getDatabasePlatform()->getTableHiLoUpdateNextValSql( + $this->_tableName, $this->_sequenceName, $this->_allocationSize + ); + + if ($conn->executeUpdate($updateSql, array(1 => $currentLevel, 2 => $currentLevel+1)) !== 1) { + // no affected rows, concurrency issue, throw exception + } + } else { + // no current level returned, TableGenerator seems to be broken, throw exception + } + } else { + // only table locks help here, implement this or throw exception? + // or do we want to work with table locks exclusively? + } + } + + return $this->_nextValue++; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php new file mode 100644 index 0000000..049cab0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php @@ -0,0 +1,118 @@ +. + */ + +namespace Doctrine\ORM\Internal; + +/** + * The CommitOrderCalculator is used by the UnitOfWork to sort out the + * correct order in which changes to entities need to be persisted. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + */ +class CommitOrderCalculator +{ + const NOT_VISITED = 1; + const IN_PROGRESS = 2; + const VISITED = 3; + + private $_nodeStates = array(); + private $_classes = array(); // The nodes to sort + private $_relatedClasses = array(); + private $_sorted = array(); + + /** + * Clears the current graph. + * + * @return void + */ + public function clear() + { + $this->_classes = + $this->_relatedClasses = array(); + } + + /** + * Gets a valid commit order for all current nodes. + * + * Uses a depth-first search (DFS) to traverse the graph. + * The desired topological sorting is the reverse postorder of these searches. + * + * @return array The list of ordered classes. + */ + public function getCommitOrder() + { + // Check whether we need to do anything. 0 or 1 node is easy. + $nodeCount = count($this->_classes); + + if ($nodeCount <= 1) { + return ($nodeCount == 1) ? array_values($this->_classes) : array(); + } + + // Init + foreach ($this->_classes as $node) { + $this->_nodeStates[$node->name] = self::NOT_VISITED; + } + + // Go + foreach ($this->_classes as $node) { + if ($this->_nodeStates[$node->name] == self::NOT_VISITED) { + $this->_visitNode($node); + } + } + + $sorted = array_reverse($this->_sorted); + + $this->_sorted = $this->_nodeStates = array(); + + return $sorted; + } + + private function _visitNode($node) + { + $this->_nodeStates[$node->name] = self::IN_PROGRESS; + + if (isset($this->_relatedClasses[$node->name])) { + foreach ($this->_relatedClasses[$node->name] as $relatedNode) { + if ($this->_nodeStates[$relatedNode->name] == self::NOT_VISITED) { + $this->_visitNode($relatedNode); + } + } + } + + $this->_nodeStates[$node->name] = self::VISITED; + $this->_sorted[] = $node; + } + + public function addDependency($fromClass, $toClass) + { + $this->_relatedClasses[$fromClass->name][] = $toClass; + } + + public function hasClass($className) + { + return isset($this->_classes[$className]); + } + + public function addClass($class) + { + $this->_classes[$class->name] = $class; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php new file mode 100644 index 0000000..83609d5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -0,0 +1,389 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use PDO, + Doctrine\DBAL\Connection, + Doctrine\DBAL\Types\Type, + Doctrine\ORM\EntityManager, + Doctrine\ORM\Events, + Doctrine\ORM\Mapping\ClassMetadata; + +/** + * Base class for all hydrators. A hydrator is a class that provides some form + * of transformation of an SQL result set into another structure. + * + * @since 2.0 + * @author Konsta Vesterinen + * @author Roman Borschel + * @author Guilherme Blanco + */ +abstract class AbstractHydrator +{ + /** @var ResultSetMapping The ResultSetMapping. */ + protected $_rsm; + + /** @var EntityManager The EntityManager instance. */ + protected $_em; + + /** @var AbstractPlatform The dbms Platform instance */ + protected $_platform; + + /** @var UnitOfWork The UnitOfWork of the associated EntityManager. */ + protected $_uow; + + /** @var array The cache used during row-by-row hydration. */ + protected $_cache = array(); + + /** @var Statement The statement that provides the data to hydrate. */ + protected $_stmt; + + /** @var array The query hints. */ + protected $_hints; + + /** + * Initializes a new instance of a class derived from AbstractHydrator. + * + * @param \Doctrine\ORM\EntityManager $em The EntityManager to use. + */ + public function __construct(EntityManager $em) + { + $this->_em = $em; + $this->_platform = $em->getConnection()->getDatabasePlatform(); + $this->_uow = $em->getUnitOfWork(); + } + + /** + * Initiates a row-by-row hydration. + * + * @param object $stmt + * @param object $resultSetMapping + * + * @return IterableResult + */ + public function iterate($stmt, $resultSetMapping, array $hints = array()) + { + $this->_stmt = $stmt; + $this->_rsm = $resultSetMapping; + $this->_hints = $hints; + + $evm = $this->_em->getEventManager(); + $evm->addEventListener(array(Events::onClear), $this); + + $this->prepare(); + + return new IterableResult($this); + } + + /** + * Hydrates all rows returned by the passed statement instance at once. + * + * @param object $stmt + * @param object $resultSetMapping + * @return mixed + */ + public function hydrateAll($stmt, $resultSetMapping, array $hints = array()) + { + $this->_stmt = $stmt; + $this->_rsm = $resultSetMapping; + $this->_hints = $hints; + + $this->prepare(); + + $result = $this->hydrateAllData(); + + $this->cleanup(); + + return $result; + } + + /** + * Hydrates a single row returned by the current statement instance during + * row-by-row hydration with {@link iterate()}. + * + * @return mixed + */ + public function hydrateRow() + { + $row = $this->_stmt->fetch(PDO::FETCH_ASSOC); + + if ( ! $row) { + $this->cleanup(); + + return false; + } + + $result = array(); + + $this->hydrateRowData($row, $this->_cache, $result); + + return $result; + } + + /** + * Excutes one-time preparation tasks, once each time hydration is started + * through {@link hydrateAll} or {@link iterate()}. + */ + protected function prepare() + {} + + /** + * Excutes one-time cleanup tasks at the end of a hydration that was initiated + * through {@link hydrateAll} or {@link iterate()}. + */ + protected function cleanup() + { + $this->_rsm = null; + + $this->_stmt->closeCursor(); + $this->_stmt = null; + } + + /** + * Hydrates a single row from the current statement instance. + * + * Template method. + * + * @param array $data The row data. + * @param array $cache The cache to use. + * @param mixed $result The result to fill. + */ + protected function hydrateRowData(array $data, array &$cache, array &$result) + { + throw new HydrationException("hydrateRowData() not implemented by this hydrator."); + } + + /** + * Hydrates all rows from the current statement instance at once. + */ + abstract protected function hydrateAllData(); + + /** + * Processes a row of the result set. + * + * Used for identity-based hydration (HYDRATE_OBJECT and HYDRATE_ARRAY). + * Puts the elements of a result row into a new array, grouped by the dql alias + * they belong to. The column names in the result set are mapped to their + * field names during this procedure as well as any necessary conversions on + * the values applied. Scalar values are kept in a specfic key 'scalars'. + * + * @param array $data SQL Result Row + * @param array &$cache Cache for column to field result information + * @param array &$id Dql-Alias => ID-Hash + * @param array &$nonemptyComponents Does this DQL-Alias has at least one non NULL value? + * + * @return array An array with all the fields (name => value) of the data row, + * grouped by their component alias. + */ + protected function gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents) + { + $rowData = array(); + + foreach ($data as $key => $value) { + // Parse each column name only once. Cache the results. + if ( ! isset($cache[$key])) { + switch (true) { + // NOTE: Most of the times it's a field mapping, so keep it first!!! + case (isset($this->_rsm->fieldMappings[$key])): + $fieldName = $this->_rsm->fieldMappings[$key]; + $classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]); + + $cache[$key]['fieldName'] = $fieldName; + $cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']); + $cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName); + $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key]; + break; + + case (isset($this->_rsm->scalarMappings[$key])): + $cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key]; + $cache[$key]['type'] = Type::getType($this->_rsm->typeMappings[$key]); + $cache[$key]['isScalar'] = true; + break; + + case (isset($this->_rsm->metaMappings[$key])): + // Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns). + $fieldName = $this->_rsm->metaMappings[$key]; + $classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$this->_rsm->columnOwnerMap[$key]]); + + $cache[$key]['isMetaColumn'] = true; + $cache[$key]['fieldName'] = $fieldName; + $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key]; + $cache[$key]['isIdentifier'] = isset($this->_rsm->isIdentifierColumn[$cache[$key]['dqlAlias']][$key]); + break; + + default: + // this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2 + // maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping. + continue 2; + } + } + + if (isset($cache[$key]['isScalar'])) { + $value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform); + + $rowData['scalars'][$cache[$key]['fieldName']] = $value; + + continue; + } + + $dqlAlias = $cache[$key]['dqlAlias']; + + if ($cache[$key]['isIdentifier']) { + $id[$dqlAlias] .= '|' . $value; + } + + if (isset($cache[$key]['isMetaColumn'])) { + if ( ! isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value !== null) { + $rowData[$dqlAlias][$cache[$key]['fieldName']] = $value; + if ($cache[$key]['isIdentifier']) { + $nonemptyComponents[$dqlAlias] = true; + } + } + + continue; + } + + // in an inheritance hierarchy the same field could be defined several times. + // We overwrite this value so long we dont have a non-null value, that value we keep. + // Per definition it cannot be that a field is defined several times and has several values. + if (isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value === null) { + continue; + } + + $rowData[$dqlAlias][$cache[$key]['fieldName']] = $cache[$key]['type']->convertToPHPValue($value, $this->_platform); + + if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) { + $nonemptyComponents[$dqlAlias] = true; + } + } + + return $rowData; + } + + /** + * Processes a row of the result set. + * + * Used for HYDRATE_SCALAR. This is a variant of _gatherRowData() that + * simply converts column names to field names and properly converts the + * values according to their types. The resulting row has the same number + * of elements as before. + * + * @param array $data + * @param array $cache + * + * @return array The processed row. + */ + protected function gatherScalarRowData(&$data, &$cache) + { + $rowData = array(); + + foreach ($data as $key => $value) { + // Parse each column name only once. Cache the results. + if ( ! isset($cache[$key])) { + switch (true) { + // NOTE: During scalar hydration, most of the times it's a scalar mapping, keep it first!!! + case (isset($this->_rsm->scalarMappings[$key])): + $cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key]; + $cache[$key]['isScalar'] = true; + break; + + case (isset($this->_rsm->fieldMappings[$key])): + $fieldName = $this->_rsm->fieldMappings[$key]; + $classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]); + + $cache[$key]['fieldName'] = $fieldName; + $cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']); + $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key]; + break; + + case (isset($this->_rsm->metaMappings[$key])): + // Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns). + $cache[$key]['isMetaColumn'] = true; + $cache[$key]['fieldName'] = $this->_rsm->metaMappings[$key]; + $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key]; + break; + + default: + // this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2 + // maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping. + continue 2; + } + } + + $fieldName = $cache[$key]['fieldName']; + + switch (true) { + case (isset($cache[$key]['isScalar'])): + $rowData[$fieldName] = $value; + break; + + case (isset($cache[$key]['isMetaColumn'])): + $rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value; + break; + + default: + $value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform); + + $rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value; + } + } + + return $rowData; + } + + /** + * Register entity as managed in UnitOfWork. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * @param object $entity + * @param array $data + * + * @todo The "$id" generation is the same of UnitOfWork#createEntity. Remove this duplication somehow + */ + protected function registerManaged(ClassMetadata $class, $entity, array $data) + { + if ($class->isIdentifierComposite) { + $id = array(); + foreach ($class->identifier as $fieldName) { + if (isset($class->associationMappings[$fieldName])) { + $id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']]; + } else { + $id[$fieldName] = $data[$fieldName]; + } + } + } else { + if (isset($class->associationMappings[$class->identifier[0]])) { + $id = array($class->identifier[0] => $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']]); + } else { + $id = array($class->identifier[0] => $data[$class->identifier[0]]); + } + } + + $this->_em->getUnitOfWork()->registerManaged($entity, $id, $data); + } + + /** + * When executed in a hydrate() loop we have to clear internal state to + * decrease memory consumption. + */ + public function onClear($eventArgs) + { + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php new file mode 100644 index 0000000..9a8fcee --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -0,0 +1,289 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use PDO, Doctrine\DBAL\Connection, Doctrine\ORM\Mapping\ClassMetadata; + +/** + * The ArrayHydrator produces a nested array "graph" that is often (not always) + * interchangeable with the corresponding object graph for read-only access. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + */ +class ArrayHydrator extends AbstractHydrator +{ + private $_ce = array(); + private $_rootAliases = array(); + private $_isSimpleQuery = false; + private $_identifierMap = array(); + private $_resultPointers = array(); + private $_idTemplate = array(); + private $_resultCounter = 0; + + /** + * {@inheritdoc} + */ + protected function prepare() + { + $this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1; + $this->_identifierMap = array(); + $this->_resultPointers = array(); + $this->_idTemplate = array(); + $this->_resultCounter = 0; + + foreach ($this->_rsm->aliasMap as $dqlAlias => $className) { + $this->_identifierMap[$dqlAlias] = array(); + $this->_resultPointers[$dqlAlias] = array(); + $this->_idTemplate[$dqlAlias] = ''; + } + } + + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + $cache = array(); + + while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { + $this->hydrateRowData($data, $cache, $result); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function hydrateRowData(array $row, array &$cache, array &$result) + { + // 1) Initialize + $id = $this->_idTemplate; // initialize the id-memory + $nonemptyComponents = array(); + $rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents); + + // Extract scalar values. They're appended at the end. + if (isset($rowData['scalars'])) { + $scalars = $rowData['scalars']; + + unset($rowData['scalars']); + + if (empty($rowData)) { + ++$this->_resultCounter; + } + } + + // 2) Now hydrate the data found in the current row. + foreach ($rowData as $dqlAlias => $data) { + $index = false; + + if (isset($this->_rsm->parentAliasMap[$dqlAlias])) { + // It's a joined result + + $parent = $this->_rsm->parentAliasMap[$dqlAlias]; + $path = $parent . '.' . $dqlAlias; + + // missing parent data, skipping as RIGHT JOIN hydration is not supported. + if ( ! isset($nonemptyComponents[$parent]) ) { + continue; + } + + // Get a reference to the right element in the result tree. + // This element will get the associated element attached. + if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) { + $first = reset($this->_resultPointers); + // TODO: Exception if $key === null ? + $baseElement =& $this->_resultPointers[$parent][key($first)]; + } else if (isset($this->_resultPointers[$parent])) { + $baseElement =& $this->_resultPointers[$parent]; + } else { + unset($this->_resultPointers[$dqlAlias]); // Ticket #1228 + continue; + } + + $relationAlias = $this->_rsm->relationMap[$dqlAlias]; + $relation = $this->getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias]; + + // Check the type of the relation (many or single-valued) + if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) { + $oneToOne = false; + + if (isset($nonemptyComponents[$dqlAlias])) { + if ( ! isset($baseElement[$relationAlias])) { + $baseElement[$relationAlias] = array(); + } + + $indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]); + $index = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false; + $indexIsValid = $index !== false ? isset($baseElement[$relationAlias][$index]) : false; + + if ( ! $indexExists || ! $indexIsValid) { + $element = $data; + if (isset($this->_rsm->indexByMap[$dqlAlias])) { + $baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element; + } else { + $baseElement[$relationAlias][] = $element; + } + + end($baseElement[$relationAlias]); + + $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = key($baseElement[$relationAlias]); + } + } else if ( ! isset($baseElement[$relationAlias])) { + $baseElement[$relationAlias] = array(); + } + } else { + $oneToOne = true; + + if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) { + $baseElement[$relationAlias] = null; + } else if ( ! isset($baseElement[$relationAlias])) { + $baseElement[$relationAlias] = $data; + } + } + + $coll =& $baseElement[$relationAlias]; + + if ($coll !== null) { + $this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne); + } + + } else { + // It's a root result element + + $this->_rootAliases[$dqlAlias] = true; // Mark as root + $entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0; + + // if this row has a NULL value for the root result id then make it a null result. + if ( ! isset($nonemptyComponents[$dqlAlias]) ) { + if ($this->_rsm->isMixed) { + $result[] = array($entityKey => null); + } else { + $result[] = null; + } + $resultKey = $this->_resultCounter; + ++$this->_resultCounter; + continue; + } + + // Check for an existing element + if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { + $element = $rowData[$dqlAlias]; + if ($this->_rsm->isMixed) { + $element = array($entityKey => $element); + } + + if (isset($this->_rsm->indexByMap[$dqlAlias])) { + $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]]; + $result[$resultKey] = $element; + } else { + $resultKey = $this->_resultCounter; + $result[] = $element; + ++$this->_resultCounter; + } + + $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey; + } else { + $index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]]; + $resultKey = $index; + /*if ($this->_rsm->isMixed) { + $result[] =& $result[$index]; + ++$this->_resultCounter; + }*/ + } + $this->updateResultPointer($result, $index, $dqlAlias, false); + } + } + + // Append scalar values to mixed result sets + if (isset($scalars)) { + if ( ! isset($resultKey) ) { + // this only ever happens when no object is fetched (scalar result only) + if (isset($this->_rsm->indexByMap['scalars'])) { + $resultKey = $row[$this->_rsm->indexByMap['scalars']]; + } else { + $resultKey = $this->_resultCounter - 1; + } + } + + foreach ($scalars as $name => $value) { + $result[$resultKey][$name] = $value; + } + } + } + + /** + * Updates the result pointer for an Entity. The result pointers point to the + * last seen instance of each Entity type. This is used for graph construction. + * + * @param array $coll The element. + * @param boolean|integer $index Index of the element in the collection. + * @param string $dqlAlias + * @param boolean $oneToOne Whether it is a single-valued association or not. + */ + private function updateResultPointer(array &$coll, $index, $dqlAlias, $oneToOne) + { + if ($coll === null) { + unset($this->_resultPointers[$dqlAlias]); // Ticket #1228 + + return; + } + + if ($index !== false) { + $this->_resultPointers[$dqlAlias] =& $coll[$index]; + + return; + } + + if ( ! $coll) { + return; + } + + if ($oneToOne) { + $this->_resultPointers[$dqlAlias] =& $coll; + + return; + } + + end($coll); + $this->_resultPointers[$dqlAlias] =& $coll[key($coll)]; + + return; + } + + /** + * Retrieve ClassMetadata associated to entity class name. + * + * @param string $className + * + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + private function getClassMetadata($className) + { + if ( ! isset($this->_ce[$className])) { + $this->_ce[$className] = $this->_em->getClassMetadata($className); + } + + return $this->_ce[$className]; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php new file mode 100644 index 0000000..147f6ac --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php @@ -0,0 +1,26 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +/** + * Represents a result structure that can be iterated over, hydrating row-by-row + * during the iteration. An IterableResult is obtained by AbstractHydrator#iterate(). + * + * @author robo + * @since 2.0 + */ +class IterableResult implements \Iterator +{ + /** + * @var \Doctrine\ORM\Internal\Hydration\AbstractHydrator + */ + private $_hydrator; + + /** + * @var boolean + */ + private $_rewinded = false; + + /** + * @var integer + */ + private $_key = -1; + + /** + * @var object + */ + private $_current = null; + + /** + * @param \Doctrine\ORM\Internal\Hydration\AbstractHydrator $hydrator + */ + public function __construct($hydrator) + { + $this->_hydrator = $hydrator; + } + + public function rewind() + { + if ($this->_rewinded == true) { + throw new HydrationException("Can only iterate a Result once."); + } else { + $this->_current = $this->next(); + $this->_rewinded = true; + } + } + + /** + * Gets the next set of results. + * + * @return array + */ + public function next() + { + $this->_current = $this->_hydrator->hydrateRow(); + $this->_key++; + return $this->_current; + } + + /** + * @return mixed + */ + public function current() + { + return $this->_current; + } + + /** + * @return int + */ + public function key() + { + return $this->_key; + } + + /** + * @return bool + */ + public function valid() + { + return ($this->_current!=false); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php new file mode 100644 index 0000000..2f960da --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -0,0 +1,536 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use PDO, + Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\PersistentCollection, + Doctrine\ORM\Query, + Doctrine\ORM\Event\LifecycleEventArgs, + Doctrine\ORM\Events, + Doctrine\Common\Collections\ArrayCollection, + Doctrine\Common\Collections\Collection, + Doctrine\ORM\Proxy\Proxy; + +/** + * The ObjectHydrator constructs an object graph out of an SQL result set. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + * + * @internal Highly performance-sensitive code. + */ +class ObjectHydrator extends AbstractHydrator +{ + /* Local ClassMetadata cache to avoid going to the EntityManager all the time. + * This local cache is maintained between hydration runs and not cleared. + */ + private $_ce = array(); + + /* The following parts are reinitialized on every hydration run. */ + + private $_identifierMap; + private $_resultPointers; + private $_idTemplate; + private $_resultCounter; + private $_rootAliases = array(); + private $_initializedCollections = array(); + private $_existingCollections = array(); + + + /** @override */ + protected function prepare() + { + $this->_identifierMap = + $this->_resultPointers = + $this->_idTemplate = array(); + + $this->_resultCounter = 0; + + if ( ! isset($this->_hints['deferEagerLoad'])) { + $this->_hints['deferEagerLoad'] = true; + } + + foreach ($this->_rsm->aliasMap as $dqlAlias => $className) { + $this->_identifierMap[$dqlAlias] = array(); + $this->_idTemplate[$dqlAlias] = ''; + + if ( ! isset($this->_ce[$className])) { + $this->_ce[$className] = $this->_em->getClassMetadata($className); + } + + // Remember which associations are "fetch joined", so that we know where to inject + // collection stubs or proxies and where not. + if ( ! isset($this->_rsm->relationMap[$dqlAlias])) { + continue; + } + + if ( ! isset($this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]])) { + throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $this->_rsm->parentAliasMap[$dqlAlias]); + } + + $sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]]; + $sourceClass = $this->_getClassMetadata($sourceClassName); + $assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]]; + + $this->_hints['fetched'][$this->_rsm->parentAliasMap[$dqlAlias]][$assoc['fieldName']] = true; + + if ($assoc['type'] === ClassMetadata::MANY_TO_MANY) { + continue; + } + + // Mark any non-collection opposite sides as fetched, too. + if ($assoc['mappedBy']) { + $this->_hints['fetched'][$dqlAlias][$assoc['mappedBy']] = true; + + continue; + } + + // handle fetch-joined owning side bi-directional one-to-one associations + if ($assoc['inversedBy']) { + $class = $this->_ce[$className]; + $inverseAssoc = $class->associationMappings[$assoc['inversedBy']]; + + if ( ! ($inverseAssoc['type'] & ClassMetadata::TO_ONE)) { + continue; + } + + $this->_hints['fetched'][$dqlAlias][$inverseAssoc['fieldName']] = true; + } + } + } + + /** + * {@inheritdoc} + */ + protected function cleanup() + { + $eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true; + + parent::cleanup(); + + $this->_identifierMap = + $this->_initializedCollections = + $this->_existingCollections = + $this->_resultPointers = array(); + + if ($eagerLoad) { + $this->_em->getUnitOfWork()->triggerEagerLoads(); + } + } + + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + $cache = array(); + + while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { + $this->hydrateRowData($row, $cache, $result); + } + + // Take snapshots from all newly initialized collections + foreach ($this->_initializedCollections as $coll) { + $coll->takeSnapshot(); + } + + return $result; + } + + /** + * Initializes a related collection. + * + * @param object $entity The entity to which the collection belongs. + * @param ClassMetadata $class + * @param string $name The name of the field on the entity that holds the collection. + * @param string $parentDqlAlias Alias of the parent fetch joining this collection. + */ + private function _initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias) + { + $oid = spl_object_hash($entity); + $relation = $class->associationMappings[$fieldName]; + $value = $class->reflFields[$fieldName]->getValue($entity); + + if ($value === null) { + $value = new ArrayCollection; + } + + if ( ! $value instanceof PersistentCollection) { + $value = new PersistentCollection( + $this->_em, $this->_ce[$relation['targetEntity']], $value + ); + $value->setOwner($entity, $relation); + + $class->reflFields[$fieldName]->setValue($entity, $value); + $this->_uow->setOriginalEntityProperty($oid, $fieldName, $value); + + $this->_initializedCollections[$oid . $fieldName] = $value; + } else if ( + isset($this->_hints[Query::HINT_REFRESH]) || + isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) && + ! $value->isInitialized() + ) { + // Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED! + $value->setDirty(false); + $value->setInitialized(true); + $value->unwrap()->clear(); + + $this->_initializedCollections[$oid . $fieldName] = $value; + } else { + // Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN! + $this->_existingCollections[$oid . $fieldName] = $value; + } + + return $value; + } + + /** + * Gets an entity instance. + * + * @param $data The instance data. + * @param $dqlAlias The DQL alias of the entity's class. + * @return object The entity. + */ + private function _getEntity(array $data, $dqlAlias) + { + $className = $this->_rsm->aliasMap[$dqlAlias]; + + if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) { + $discrColumn = $this->_rsm->metaMappings[$this->_rsm->discriminatorColumns[$dqlAlias]]; + + if ($data[$discrColumn] === "") { + throw HydrationException::emptyDiscriminatorValue($dqlAlias); + } + + $className = $this->_ce[$className]->discriminatorMap[$data[$discrColumn]]; + + unset($data[$discrColumn]); + } + + if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->_rootAliases[$dqlAlias])) { + $this->registerManaged($this->_ce[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data); + } + + $this->_hints['fetchAlias'] = $dqlAlias; + + return $this->_uow->createEntity($className, $data, $this->_hints); + } + + private function _getEntityFromIdentityMap($className, array $data) + { + // TODO: Abstract this code and UnitOfWork::createEntity() equivalent? + $class = $this->_ce[$className]; + + /* @var $class ClassMetadata */ + if ($class->isIdentifierComposite) { + $idHash = ''; + foreach ($class->identifier as $fieldName) { + if (isset($class->associationMappings[$fieldName])) { + $idHash .= $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] . ' '; + } else { + $idHash .= $data[$fieldName] . ' '; + } + } + return $this->_uow->tryGetByIdHash(rtrim($idHash), $class->rootEntityName); + } else if (isset($class->associationMappings[$class->identifier[0]])) { + return $this->_uow->tryGetByIdHash($data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']], $class->rootEntityName); + } else { + return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName); + } + } + + /** + * Gets a ClassMetadata instance from the local cache. + * If the instance is not yet in the local cache, it is loaded into the + * local cache. + * + * @param string $className The name of the class. + * @return ClassMetadata + */ + private function _getClassMetadata($className) + { + if ( ! isset($this->_ce[$className])) { + $this->_ce[$className] = $this->_em->getClassMetadata($className); + } + + return $this->_ce[$className]; + } + + /** + * Hydrates a single row in an SQL result set. + * + * @internal + * First, the data of the row is split into chunks where each chunk contains data + * that belongs to a particular component/class. Afterwards, all these chunks + * are processed, one after the other. For each chunk of class data only one of the + * following code paths is executed: + * + * Path A: The data chunk belongs to a joined/associated object and the association + * is collection-valued. + * Path B: The data chunk belongs to a joined/associated object and the association + * is single-valued. + * Path C: The data chunk belongs to a root result element/object that appears in the topmost + * level of the hydrated result. A typical example are the objects of the type + * specified by the FROM clause in a DQL query. + * + * @param array $data The data of the row to process. + * @param array $cache The cache to use. + * @param array $result The result array to fill. + */ + protected function hydrateRowData(array $row, array &$cache, array &$result) + { + // Initialize + $id = $this->_idTemplate; // initialize the id-memory + $nonemptyComponents = array(); + // Split the row data into chunks of class data. + $rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents); + + // Extract scalar values. They're appended at the end. + if (isset($rowData['scalars'])) { + $scalars = $rowData['scalars']; + + unset($rowData['scalars']); + + if (empty($rowData)) { + ++$this->_resultCounter; + } + } + + // Hydrate the data chunks + foreach ($rowData as $dqlAlias => $data) { + $entityName = $this->_rsm->aliasMap[$dqlAlias]; + + if (isset($this->_rsm->parentAliasMap[$dqlAlias])) { + // It's a joined result + + $parentAlias = $this->_rsm->parentAliasMap[$dqlAlias]; + // we need the $path to save into the identifier map which entities were already + // seen for this parent-child relationship + $path = $parentAlias . '.' . $dqlAlias; + + // We have a RIGHT JOIN result here. Doctrine cannot hydrate RIGHT JOIN Object-Graphs + if (!isset($nonemptyComponents[$parentAlias])) { + // TODO: Add special case code where we hydrate the right join objects into identity map at least + continue; + } + + // Get a reference to the parent object to which the joined element belongs. + if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) { + $first = reset($this->_resultPointers); + $parentObject = $first[key($first)]; + } else if (isset($this->_resultPointers[$parentAlias])) { + $parentObject = $this->_resultPointers[$parentAlias]; + } else { + // Parent object of relation not found, so skip it. + continue; + } + + $parentClass = $this->_ce[$this->_rsm->aliasMap[$parentAlias]]; + $oid = spl_object_hash($parentObject); + $relationField = $this->_rsm->relationMap[$dqlAlias]; + $relation = $parentClass->associationMappings[$relationField]; + $reflField = $parentClass->reflFields[$relationField]; + + // Check the type of the relation (many or single-valued) + if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) { + $reflFieldValue = $reflField->getValue($parentObject); + // PATH A: Collection-valued association + if (isset($nonemptyComponents[$dqlAlias])) { + $collKey = $oid . $relationField; + if (isset($this->_initializedCollections[$collKey])) { + $reflFieldValue = $this->_initializedCollections[$collKey]; + } else if ( ! isset($this->_existingCollections[$collKey])) { + $reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); + } + + $indexExists = isset($this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]); + $index = $indexExists ? $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false; + $indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false; + + if ( ! $indexExists || ! $indexIsValid) { + if (isset($this->_existingCollections[$collKey])) { + // Collection exists, only look for the element in the identity map. + if ($element = $this->_getEntityFromIdentityMap($entityName, $data)) { + $this->_resultPointers[$dqlAlias] = $element; + } else { + unset($this->_resultPointers[$dqlAlias]); + } + } else { + $element = $this->_getEntity($data, $dqlAlias); + + if (isset($this->_rsm->indexByMap[$dqlAlias])) { + $indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]]; + $reflFieldValue->hydrateSet($indexValue, $element); + $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue; + } else { + $reflFieldValue->hydrateAdd($element); + $reflFieldValue->last(); + $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key(); + } + // Update result pointer + $this->_resultPointers[$dqlAlias] = $element; + } + } else { + // Update result pointer + $this->_resultPointers[$dqlAlias] = $reflFieldValue[$index]; + } + } else if ( ! $reflFieldValue) { + $reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); + } else if ($reflFieldValue instanceof PersistentCollection && $reflFieldValue->isInitialized() === false) { + $reflFieldValue->setInitialized(true); + } + + } else { + // PATH B: Single-valued association + $reflFieldValue = $reflField->getValue($parentObject); + if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || ($reflFieldValue instanceof Proxy && !$reflFieldValue->__isInitialized__)) { + // we only need to take action if this value is null, + // we refresh the entity or its an unitialized proxy. + if (isset($nonemptyComponents[$dqlAlias])) { + $element = $this->_getEntity($data, $dqlAlias); + $reflField->setValue($parentObject, $element); + $this->_uow->setOriginalEntityProperty($oid, $relationField, $element); + $targetClass = $this->_ce[$relation['targetEntity']]; + + if ($relation['isOwningSide']) { + //TODO: Just check hints['fetched'] here? + // If there is an inverse mapping on the target class its bidirectional + if ($relation['inversedBy']) { + $inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']]; + if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) { + $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($element, $parentObject); + $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc['fieldName'], $parentObject); + } + } else if ($parentClass === $targetClass && $relation['mappedBy']) { + // Special case: bi-directional self-referencing one-one on the same class + $targetClass->reflFields[$relationField]->setValue($element, $parentObject); + } + } else { + // For sure bidirectional, as there is no inverse side in unidirectional mappings + $targetClass->reflFields[$relation['mappedBy']]->setValue($element, $parentObject); + $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject); + } + // Update result pointer + $this->_resultPointers[$dqlAlias] = $element; + } else { + $this->_uow->setOriginalEntityProperty($oid, $relationField, null); + } + // else leave $reflFieldValue null for single-valued associations + } else { + // Update result pointer + $this->_resultPointers[$dqlAlias] = $reflFieldValue; + } + } + } else { + // PATH C: Its a root result element + $this->_rootAliases[$dqlAlias] = true; // Mark as root alias + $entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0; + + // if this row has a NULL value for the root result id then make it a null result. + if ( ! isset($nonemptyComponents[$dqlAlias]) ) { + if ($this->_rsm->isMixed) { + $result[] = array($entityKey => null); + } else { + $result[] = null; + } + $resultKey = $this->_resultCounter; + ++$this->_resultCounter; + continue; + } + + // check for existing result from the iterations before + if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { + $element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias); + if ($this->_rsm->isMixed) { + $element = array($entityKey => $element); + } + + if (isset($this->_rsm->indexByMap[$dqlAlias])) { + $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]]; + + if (isset($this->_hints['collection'])) { + $this->_hints['collection']->hydrateSet($resultKey, $element); + } + + $result[$resultKey] = $element; + } else { + $resultKey = $this->_resultCounter; + ++$this->_resultCounter; + + if (isset($this->_hints['collection'])) { + $this->_hints['collection']->hydrateAdd($element); + } + + $result[] = $element; + } + + $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey; + + // Update result pointer + $this->_resultPointers[$dqlAlias] = $element; + + } else { + // Update result pointer + $index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]]; + $this->_resultPointers[$dqlAlias] = $result[$index]; + $resultKey = $index; + /*if ($this->_rsm->isMixed) { + $result[] = $result[$index]; + ++$this->_resultCounter; + }*/ + } + } + } + + // Append scalar values to mixed result sets + if (isset($scalars)) { + if ( ! isset($resultKey) ) { + if (isset($this->_rsm->indexByMap['scalars'])) { + $resultKey = $row[$this->_rsm->indexByMap['scalars']]; + } else { + $resultKey = $this->_resultCounter - 1; + } + } + + foreach ($scalars as $name => $value) { + $result[$resultKey][$name] = $value; + } + } + } + + /** + * When executed in a hydrate() loop we may have to clear internal state to + * decrease memory consumption. + */ + public function onClear($eventArgs) + { + parent::onClear($eventArgs); + + $aliases = array_keys($this->_identifierMap); + $this->_identifierMap = array(); + + foreach ($aliases as $alias) { + $this->_identifierMap[$alias] = array(); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php new file mode 100644 index 0000000..d02651b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use Doctrine\DBAL\Connection; + +/** + * Hydrator that produces flat, rectangular results of scalar data. + * The created result is almost the same as a regular SQL result set, except + * that column names are mapped to field names and data type conversions take place. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + */ +class ScalarHydrator extends AbstractHydrator +{ + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + $cache = array(); + + while ($data = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) { + $this->hydrateRowData($data, $cache, $result); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function hydrateRowData(array $data, array &$cache, array &$result) + { + $result[] = $this->gatherScalarRowData($data, $cache); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php new file mode 100644 index 0000000..09c2c7f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php @@ -0,0 +1,183 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use \PDO, + Doctrine\DBAL\Types\Type, + Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\Event\LifecycleEventArgs, + Doctrine\ORM\Events, + Doctrine\ORM\Query; + +class SimpleObjectHydrator extends AbstractHydrator +{ + /** + * @var ClassMetadata + */ + private $class; + + /** + * @var array + */ + private $declaringClasses = array(); + + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + $cache = array(); + + while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { + $this->hydrateRowData($row, $cache, $result); + } + + $this->_em->getUnitOfWork()->triggerEagerLoads(); + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function prepare() + { + if (count($this->_rsm->aliasMap) !== 1) { + throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains more than one object result."); + } + + if ($this->_rsm->scalarMappings) { + throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings."); + } + + $this->class = $this->_em->getClassMetadata(reset($this->_rsm->aliasMap)); + + // We only need to add declaring classes if we have inheritance. + if ($this->class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_NONE) { + return; + } + + foreach ($this->_rsm->declaringClasses AS $column => $class) { + $this->declaringClasses[$column] = $this->_em->getClassMetadata($class); + } + } + + /** + * {@inheritdoc} + */ + protected function hydrateRowData(array $sqlResult, array &$cache, array &$result) + { + $entityName = $this->class->name; + $data = array(); + + // We need to find the correct entity class name if we have inheritance in resultset + if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) { + $discrColumnName = $this->_platform->getSQLResultCasing($this->class->discriminatorColumn['name']); + + if ($sqlResult[$discrColumnName] === '') { + throw HydrationException::emptyDiscriminatorValue(key($this->_rsm->aliasMap)); + } + + $entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]]; + + unset($sqlResult[$discrColumnName]); + } + + foreach ($sqlResult as $column => $value) { + // Hydrate column information if not yet present + if ( ! isset($cache[$column])) { + if (($info = $this->hydrateColumnInfo($entityName, $column)) === null) { + continue; + } + + $cache[$column] = $info; + } + + // Convert field to a valid PHP value + if (isset($cache[$column]['field'])) { + $type = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type']); + $value = $type->convertToPHPValue($value, $this->_platform); + } + + // Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator) + if (isset($cache[$column]) && ( ! isset($data[$cache[$column]['name']]) || $value !== null)) { + $data[$cache[$column]['name']] = $value; + } + } + + if (isset($this->_hints[Query::HINT_REFRESH_ENTITY])) { + $this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data); + } + + $uow = $this->_em->getUnitOfWork(); + $entity = $uow->createEntity($entityName, $data, $this->_hints); + + $result[] = $entity; + } + + /** + * Retrieve column information form ResultSetMapping. + * + * @param string $entityName + * @param string $column + * + * @return array + */ + protected function hydrateColumnInfo($entityName, $column) + { + switch (true) { + case (isset($this->_rsm->fieldMappings[$column])): + $class = isset($this->declaringClasses[$column]) + ? $this->declaringClasses[$column] + : $this->class; + + // If class is not part of the inheritance, ignore + if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) { + return null; + } + + return array( + 'class' => $class, + 'name' => $this->_rsm->fieldMappings[$column], + 'field' => true, + ); + + case (isset($this->_rsm->relationMap[$column])): + $class = isset($this->_rsm->relationMap[$column]) + ? $this->_rsm->relationMap[$column] + : $this->class; + + // If class is not self referencing, ignore + if ( ! ($class === $entityName || is_subclass_of($entityName, $class))) { + return null; + } + + // TODO: Decide what to do with associations. It seems original code is incomplete. + // One solution is to load the association, but it might require extra efforts. + return array('name' => $column); + + default: + return array( + 'name' => $this->_rsm->metaMappings[$column] + ); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php new file mode 100644 index 0000000..a5dabc6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use Doctrine\DBAL\Connection, + Doctrine\ORM\NoResultException, + Doctrine\ORM\NonUniqueResultException; + +/** + * Hydrator that hydrates a single scalar value from the result set. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + */ +class SingleScalarHydrator extends AbstractHydrator +{ + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $data = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC); + $numRows = count($data); + + if ($numRows === 0) { + throw new NoResultException(); + } + + if ($numRows > 1 || count($data[key($data)]) > 1) { + throw new NonUniqueResultException(); + } + + $cache = array(); + $result = $this->gatherScalarRowData($data[key($data)], $cache); + + return array_shift($result); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Annotation.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Annotation.php new file mode 100644 index 0000000..dd8f4ef --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Annotation.php @@ -0,0 +1,24 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +interface Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php new file mode 100644 index 0000000..843e2ce --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php @@ -0,0 +1,167 @@ +. + */ + + +namespace Doctrine\ORM\Mapping\Builder; + +use Doctrine\ORM\Mapping\ClassMetadata; + +class AssociationBuilder +{ + /** + * @var ClassMetadataBuilder + */ + protected $builder; + + /** + * @var array + */ + protected $mapping; + + /** + * @var array + */ + protected $joinColumns; + + /** + * + * @var int + */ + protected $type; + + /** + * @param ClassMetadataBuilder $builder + * @param array $mapping + */ + public function __construct(ClassMetadataBuilder $builder, array $mapping, $type) + { + $this->builder = $builder; + $this->mapping = $mapping; + $this->type = $type; + } + + public function mappedBy($fieldName) + { + $this->mapping['mappedBy'] = $fieldName; + return $this; + } + + public function inversedBy($fieldName) + { + $this->mapping['inversedBy'] = $fieldName; + return $this; + } + + public function cascadeAll() + { + $this->mapping['cascade'] = array("ALL"); + return $this; + } + + public function cascadePersist() + { + $this->mapping['cascade'][] = "persist"; + return $this; + } + + public function cascadeRemove() + { + $this->mapping['cascade'][] = "remove"; + return $this; + } + + public function cascadeMerge() + { + $this->mapping['cascade'][] = "merge"; + return $this; + } + + public function cascadeDetach() + { + $this->mapping['cascade'][] = "detach"; + return $this; + } + + public function cascadeRefresh() + { + $this->mapping['cascade'][] = "refresh"; + return $this; + } + + public function fetchExtraLazy() + { + $this->mapping['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; + return $this; + } + + public function fetchEager() + { + $this->mapping['fetch'] = ClassMetadata::FETCH_EAGER; + return $this; + } + + public function fetchLazy() + { + $this->mapping['fetch'] = ClassMetadata::FETCH_LAZY; + return $this; + } + + /** + * Add Join Columns + * + * @param string $columnName + * @param string $referencedColumnName + * @param bool $nullable + * @param bool $unique + * @param string $onDelete + * @param string $columnDef + */ + public function addJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null) + { + $this->joinColumns[] = array( + 'name' => $columnName, + 'referencedColumnName' => $referencedColumnName, + 'nullable' => $nullable, + 'unique' => $unique, + 'onDelete' => $onDelete, + 'columnDefinition' => $columnDef, + ); + return $this; + } + + /** + * @return ClassMetadataBuilder + */ + public function build() + { + $mapping = $this->mapping; + if ($this->joinColumns) { + $mapping['joinColumns'] = $this->joinColumns; + } + $cm = $this->builder->getClassMetadata(); + if ($this->type == ClassMetadata::MANY_TO_ONE) { + $cm->mapManyToOne($mapping); + } else if ($this->type == ClassMetadata::ONE_TO_ONE) { + $cm->mapOneToOne($mapping); + } else { + throw new \InvalidArgumentException("Type should be a ToOne Assocation here"); + } + return $this->builder; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php new file mode 100644 index 0000000..fb1f1d5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php @@ -0,0 +1,470 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +use Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * Builder Object for ClassMetadata + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + */ +class ClassMetadataBuilder +{ + /** + * @var \Doctrine\ORM\Mapping\ClassMetadataInfo + */ + private $cm; + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $cm + */ + public function __construct(ClassMetadataInfo $cm) + { + $this->cm = $cm; + } + + /** + * @return ClassMetadata + */ + public function getClassMetadata() + { + return $this->cm; + } + + /** + * Mark the class as mapped superclass. + * + * @return ClassMetadataBuilder + */ + public function setMappedSuperClass() + { + $this->cm->isMappedSuperclass = true; + + return $this; + } + + /** + * Set custom Repository class name + * + * @param string $repositoryClassName + * @return ClassMetadataBuilder + */ + public function setCustomRepositoryClass($repositoryClassName) + { + $this->cm->setCustomRepositoryClass($repositoryClassName); + + return $this; + } + + /** + * Mark class read only + * + * @return ClassMetadataBuilder + */ + public function setReadOnly() + { + $this->cm->markReadOnly(); + + return $this; + } + + /** + * Set the table name + * + * @param string $name + * @return ClassMetadataBuilder + */ + public function setTable($name) + { + $this->cm->setPrimaryTable(array('name' => $name)); + + return $this; + } + + /** + * Add Index + * + * @param array $columns + * @param string $name + * @return ClassMetadataBuilder + */ + public function addIndex(array $columns, $name) + { + if (!isset($this->cm->table['indexes'])) { + $this->cm->table['indexes'] = array(); + } + + $this->cm->table['indexes'][$name] = array('columns' => $columns); + + return $this; + } + + /** + * Add Unique Constraint + * + * @param array $columns + * @param string $name + * @return ClassMetadataBuilder + */ + public function addUniqueConstraint(array $columns, $name) + { + if ( ! isset($this->cm->table['uniqueConstraints'])) { + $this->cm->table['uniqueConstraints'] = array(); + } + + $this->cm->table['uniqueConstraints'][$name] = array('columns' => $columns); + + return $this; + } + + /** + * Add named query + * + * @param string $name + * @param string $dqlQuery + * @return ClassMetadataBuilder + */ + public function addNamedQuery($name, $dqlQuery) + { + $this->cm->addNamedQuery(array( + 'name' => $name, + 'query' => $dqlQuery, + )); + + return $this; + } + + /** + * Set class as root of a joined table inheritance hierachy. + * + * @return ClassMetadataBuilder + */ + public function setJoinedTableInheritance() + { + $this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_JOINED); + + return $this; + } + + /** + * Set class as root of a single table inheritance hierachy. + * + * @return ClassMetadataBuilder + */ + public function setSingleTableInheritance() + { + $this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE); + + return $this; + } + + /** + * Set the discriminator column details. + * + * @param string $name + * @param string $type + */ + public function setDiscriminatorColumn($name, $type = 'string', $length = 255) + { + $this->cm->setDiscriminatorColumn(array( + 'name' => $name, + 'type' => $type, + 'length' => $length, + )); + + return $this; + } + + /** + * Add a subclass to this inheritance hierachy. + * + * @param string $name + * @param string $class + * @return ClassMetadataBuilder + */ + public function addDiscriminatorMapClass($name, $class) + { + $this->cm->addDiscriminatorMapClass($name, $class); + + return $this; + } + + /** + * Set deferred explicit change tracking policy. + * + * @return ClassMetadataBuilder + */ + public function setChangeTrackingPolicyDeferredExplicit() + { + $this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT); + + return $this; + } + + /** + * Set notify change tracking policy. + * + * @return ClassMetadataBuilder + */ + public function setChangeTrackingPolicyNotify() + { + $this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_NOTIFY); + + return $this; + } + + /** + * Add lifecycle event + * + * @param string $methodName + * @param string $event + * @return ClassMetadataBuilder + */ + public function addLifecycleEvent($methodName, $event) + { + $this->cm->addLifecycleCallback($methodName, $event); + + return $this; + } + + /** + * Add Field + * + * @param string $name + * @param string $type + * @param array $mapping + */ + public function addField($name, $type, array $mapping = array()) + { + $mapping['fieldName'] = $name; + $mapping['type'] = $type; + + $this->cm->mapField($mapping); + + return $this; + } + + /** + * Create a field builder. + * + * @param string $name + * @param string $type + * @return FieldBuilder + */ + public function createField($name, $type) + { + return new FieldBuilder( + $this, + array( + 'fieldName' => $name, + 'type' => $type + ) + ); + } + + /** + * Add a simple many to one association, optionally with the inversed by field. + * + * @param string $name + * @param string $targetEntity + * @param string|null $inversedBy + * @return ClassMetadataBuilder + */ + public function addManyToOne($name, $targetEntity, $inversedBy = null) + { + $builder = $this->createManyToOne($name, $targetEntity); + + if ($inversedBy) { + $builder->inversedBy($inversedBy); + } + + return $builder->build(); + } + + /** + * Create a ManyToOne Assocation Builder. + * + * Note: This method does not add the association, you have to call build() on the AssociationBuilder. + * + * @param string $name + * @param string $targetEntity + * @return AssociationBuilder + */ + public function createManyToOne($name, $targetEntity) + { + return new AssociationBuilder( + $this, + array( + 'fieldName' => $name, + 'targetEntity' => $targetEntity + ), + ClassMetadata::MANY_TO_ONE + ); + } + + /** + * Create OneToOne Assocation Builder + * + * @param string $name + * @param string $targetEntity + * @return AssociationBuilder + */ + public function createOneToOne($name, $targetEntity) + { + return new AssociationBuilder( + $this, + array( + 'fieldName' => $name, + 'targetEntity' => $targetEntity + ), + ClassMetadata::ONE_TO_ONE + ); + } + + /** + * Add simple inverse one-to-one assocation. + * + * @param string $name + * @param string $targetEntity + * @param string $mappedBy + * @return ClassMetadataBuilder + */ + public function addInverseOneToOne($name, $targetEntity, $mappedBy) + { + $builder = $this->createOneToOne($name, $targetEntity); + $builder->mappedBy($mappedBy); + + return $builder->build(); + } + + /** + * Add simple owning one-to-one assocation. + * + * @param string $name + * @param string $targetEntity + * @param string $inversedBy + * @return ClassMetadataBuilder + */ + public function addOwningOneToOne($name, $targetEntity, $inversedBy = null) + { + $builder = $this->createOneToOne($name, $targetEntity); + + if ($inversedBy) { + $builder->inversedBy($inversedBy); + } + + return $builder->build(); + } + + /** + * Create ManyToMany Assocation Builder + * + * @param string $name + * @param string $targetEntity + * @return ManyToManyAssociationBuilder + */ + public function createManyToMany($name, $targetEntity) + { + return new ManyToManyAssociationBuilder( + $this, + array( + 'fieldName' => $name, + 'targetEntity' => $targetEntity + ), + ClassMetadata::MANY_TO_MANY + ); + } + + /** + * Add a simple owning many to many assocation. + * + * @param string $name + * @param string $targetEntity + * @param string|null $inversedBy + * @return ClassMetadataBuilder + */ + public function addOwningManyToMany($name, $targetEntity, $inversedBy = null) + { + $builder = $this->createManyToMany($name, $targetEntity); + + if ($inversedBy) { + $builder->inversedBy($inversedBy); + } + + return $builder->build(); + } + + /** + * Add a simple inverse many to many assocation. + * + * @param string $name + * @param string $targetEntity + * @param string $mappedBy + * @return ClassMetadataBuilder + */ + public function addInverseManyToMany($name, $targetEntity, $mappedBy) + { + $builder = $this->createManyToMany($name, $targetEntity); + $builder->mappedBy($mappedBy); + + return $builder->build(); + } + + /** + * Create a one to many assocation builder + * + * @param string $name + * @param string $targetEntity + * @return OneToManyAssociationBuilder + */ + public function createOneToMany($name, $targetEntity) + { + return new OneToManyAssociationBuilder( + $this, + array( + 'fieldName' => $name, + 'targetEntity' => $targetEntity + ), + ClassMetadata::ONE_TO_MANY + ); + } + + /** + * Add simple OneToMany assocation. + * + * @param string $name + * @param string $targetEntity + * @param string $mappedBy + * @return ClassMetadataBuilder + */ + public function addOneToMany($name, $targetEntity, $mappedBy) + { + $builder = $this->createOneToMany($name, $targetEntity); + $builder->mappedBy($mappedBy); + + return $builder->build(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php new file mode 100644 index 0000000..3f4a5bb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php @@ -0,0 +1,223 @@ +. + */ + + +namespace Doctrine\ORM\Mapping\Builder; + +/** + * Field Builder + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.2 + * @author Benjamin Eberlei + */ +class FieldBuilder +{ + /** + * @var ClassMetadataBuilder + */ + private $builder; + /** + * @var array + */ + private $mapping; + /** + * @var bool + */ + private $version; + + /** + * @var string + */ + private $generatedValue; + + /** + * @var array + */ + private $sequenceDef; + + /** + * + * @param ClassMetadataBuilder $builder + * @param array $mapping + */ + public function __construct(ClassMetadataBuilder $builder, array $mapping) + { + $this->builder = $builder; + $this->mapping = $mapping; + } + + /** + * Set length. + * + * @param int $length + * @return FieldBuilder + */ + public function length($length) + { + $this->mapping['length'] = $length; + return $this; + } + + /** + * Set nullable + * + * @param bool + * @return FieldBuilder + */ + public function nullable($flag = true) + { + $this->mapping['nullable'] = (bool)$flag; + return $this; + } + + /** + * Set Unique + * + * @param bool + * @return FieldBuilder + */ + public function unique($flag = true) + { + $this->mapping['unique'] = (bool)$flag; + return $this; + } + + /** + * Set column name + * + * @param string $name + * @return FieldBuilder + */ + public function columnName($name) + { + $this->mapping['columnName'] = $name; + return $this; + } + + /** + * Set Precision + * + * @param int $p + * @return FieldBuilder + */ + public function precision($p) + { + $this->mapping['precision'] = $p; + return $this; + } + + /** + * Set scale. + * + * @param int $s + * @return FieldBuilder + */ + public function scale($s) + { + $this->mapping['scale'] = $s; + return $this; + } + + /** + * Set field as primary key. + * + * @return FieldBuilder + */ + public function isPrimaryKey() + { + $this->mapping['id'] = true; + return $this; + } + + /** + * @param int $strategy + * @return FieldBuilder + */ + public function generatedValue($strategy = 'AUTO') + { + $this->generatedValue = $strategy; + return $this; + } + + /** + * Set field versioned + * + * @return FieldBuilder + */ + public function isVersionField() + { + $this->version = true; + return $this; + } + + /** + * Set Sequence Generator + * + * @param string $sequenceName + * @param int $allocationSize + * @param int $initialValue + * @return FieldBuilder + */ + public function setSequenceGenerator($sequenceName, $allocationSize = 1, $initialValue = 1) + { + $this->sequenceDef = array( + 'sequenceName' => $sequenceName, + 'allocationSize' => $allocationSize, + 'initialValue' => $initialValue, + ); + return $this; + } + + /** + * Set column definition. + * + * @param string $def + * @return FieldBuilder + */ + public function columnDefinition($def) + { + $this->mapping['columnDefinition'] = $def; + return $this; + } + + /** + * Finalize this field and attach it to the ClassMetadata. + * + * Without this call a FieldBuilder has no effect on the ClassMetadata. + * + * @return ClassMetadataBuilder + */ + public function build() + { + $cm = $this->builder->getClassMetadata(); + if ($this->generatedValue) { + $cm->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $this->generatedValue)); + } + if ($this->version) { + $cm->setVersionMapping($this->mapping); + } + $cm->mapField($this->mapping); + if ($this->sequenceDef) { + $cm->setSequenceGeneratorDefinition($this->sequenceDef); + } + return $this->builder; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php new file mode 100644 index 0000000..c1dd1e5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php @@ -0,0 +1,86 @@ +. + */ + + +namespace Doctrine\ORM\Mapping\Builder; + +/** + * ManyToMany Association Builder + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder +{ + private $joinTableName; + + private $inverseJoinColumns = array(); + + public function setJoinTable($name) + { + $this->joinTableName = $name; + return $this; + } + + /** + * Add Inverse Join Columns + * + * @param string $columnName + * @param string $referencedColumnName + * @param bool $nullable + * @param bool $unique + * @param string $onDelete + * @param string $columnDef + */ + public function addInverseJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null) + { + $this->inverseJoinColumns[] = array( + 'name' => $columnName, + 'referencedColumnName' => $referencedColumnName, + 'nullable' => $nullable, + 'unique' => $unique, + 'onDelete' => $onDelete, + 'columnDefinition' => $columnDef, + ); + return $this; + } + + /** + * @return ClassMetadataBuilder + */ + public function build() + { + $mapping = $this->mapping; + $mapping['joinTable'] = array(); + if ($this->joinColumns) { + $mapping['joinTable']['joinColumns'] = $this->joinColumns; + } + if ($this->inverseJoinColumns) { + $mapping['joinTable']['inverseJoinColumns'] = $this->inverseJoinColumns; + } + if ($this->joinTableName) { + $mapping['joinTable']['name'] = $this->joinTableName; + } + $cm = $this->builder->getClassMetadata(); + $cm->mapManyToMany($mapping); + return $this->builder; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php new file mode 100644 index 0000000..be55c2d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php @@ -0,0 +1,62 @@ +. + */ + + +namespace Doctrine\ORM\Mapping\Builder; + +/** + * OneToMany Association Builder + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class OneToManyAssociationBuilder extends AssociationBuilder +{ + /** + * @param array $fieldNames + * @return OneToManyAssociationBuilder + */ + public function setOrderBy(array $fieldNames) + { + $this->mapping['orderBy'] = $fieldNames; + return $this; + } + + public function setIndexBy($fieldName) + { + $this->mapping['indexBy'] = $fieldName; + return $this; + } + + /** + * @return ClassMetadataBuilder + */ + public function build() + { + $mapping = $this->mapping; + if ($this->joinColumns) { + $mapping['joinColumns'] = $this->joinColumns; + } + $cm = $this->builder->getClassMetadata(); + $cm->mapOneToMany($mapping); + return $this->builder; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php new file mode 100644 index 0000000..a9cc372 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class ChangeTrackingPolicy implements Annotation +{ + /** @var string */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadata.php new file mode 100644 index 0000000..70b8f9e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use ReflectionClass, ReflectionProperty; + +/** + * A ClassMetadata instance holds all the object-relational mapping metadata + * of an entity and it's associations. + * + * Once populated, ClassMetadata instances are usually cached in a serialized form. + * + * IMPORTANT NOTE: + * + * The fields of this class are only public for 2 reasons: + * 1) To allow fast READ access. + * 2) To drastically reduce the size of a serialized instance (private/protected members + * get the whole class name, namespace inclusive, prepended to every property in + * the serialized representation). + * + * @author Roman Borschel + * @author Jonathan H. Wage + * @since 2.0 + */ +class ClassMetadata extends ClassMetadataInfo +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php new file mode 100644 index 0000000..e2a5621 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -0,0 +1,593 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use ReflectionException, + Doctrine\ORM\ORMException, + Doctrine\ORM\EntityManager, + Doctrine\DBAL\Platforms, + Doctrine\ORM\Events, + Doctrine\Common\Persistence\Mapping\RuntimeReflectionService, + Doctrine\Common\Persistence\Mapping\ReflectionService, + Doctrine\Common\Persistence\Mapping\ClassMetadataFactory as ClassMetadataFactoryInterface; + +/** + * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the + * metadata mapping informations of a class which describes how a class should be mapped + * to a relational database. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ClassMetadataFactory implements ClassMetadataFactoryInterface +{ + /** + * @var EntityManager + */ + private $em; + + /** + * @var AbstractPlatform + */ + private $targetPlatform; + + /** + * @var \Doctrine\ORM\Mapping\Driver\Driver + */ + private $driver; + + /** + * @var \Doctrine\Common\EventManager + */ + private $evm; + + /** + * @var \Doctrine\Common\Cache\Cache + */ + private $cacheDriver; + + /** + * @var array + */ + private $loadedMetadata = array(); + + /** + * @var bool + */ + private $initialized = false; + + /** + * @var ReflectionService + */ + private $reflectionService; + + /** + * @param EntityManager $$em + */ + public function setEntityManager(EntityManager $em) + { + $this->em = $em; + } + + /** + * Sets the cache driver used by the factory to cache ClassMetadata instances. + * + * @param \Doctrine\Common\Cache\Cache $cacheDriver + */ + public function setCacheDriver($cacheDriver) + { + $this->cacheDriver = $cacheDriver; + } + + /** + * Gets the cache driver used by the factory to cache ClassMetadata instances. + * + * @return \Doctrine\Common\Cache\Cache + */ + public function getCacheDriver() + { + return $this->cacheDriver; + } + + public function getLoadedMetadata() + { + return $this->loadedMetadata; + } + + /** + * Forces the factory to load the metadata of all classes known to the underlying + * mapping driver. + * + * @return array The ClassMetadata instances of all mapped classes. + */ + public function getAllMetadata() + { + if ( ! $this->initialized) { + $this->initialize(); + } + + $metadata = array(); + foreach ($this->driver->getAllClassNames() as $className) { + $metadata[] = $this->getMetadataFor($className); + } + + return $metadata; + } + + /** + * Lazy initialization of this stuff, especially the metadata driver, + * since these are not needed at all when a metadata cache is active. + */ + private function initialize() + { + $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl(); + $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform(); + $this->evm = $this->em->getEventManager(); + $this->initialized = true; + } + + /** + * Gets the class metadata descriptor for a class. + * + * @param string $className The name of the class. + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + public function getMetadataFor($className) + { + if ( ! isset($this->loadedMetadata[$className])) { + $realClassName = $className; + + // Check for namespace alias + if (strpos($className, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $className); + $realClassName = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + + if (isset($this->loadedMetadata[$realClassName])) { + // We do not have the alias name in the map, include it + $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + + return $this->loadedMetadata[$realClassName]; + } + } + + if ($this->cacheDriver) { + if (($cached = $this->cacheDriver->fetch("$realClassName\$CLASSMETADATA")) !== false) { + $this->wakeupReflection($cached, $this->getReflectionService()); + $this->loadedMetadata[$realClassName] = $cached; + } else { + foreach ($this->loadMetadata($realClassName) as $loadedClassName) { + $this->cacheDriver->save( + "$loadedClassName\$CLASSMETADATA", $this->loadedMetadata[$loadedClassName], null + ); + } + } + } else { + $this->loadMetadata($realClassName); + } + + if ($className != $realClassName) { + // We do not have the alias name in the map, include it + $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + } + } + + return $this->loadedMetadata[$className]; + } + + /** + * Checks whether the factory has the metadata for a class loaded already. + * + * @param string $className + * @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise. + */ + public function hasMetadataFor($className) + { + return isset($this->loadedMetadata[$className]); + } + + /** + * Sets the metadata descriptor for a specific class. + * + * NOTE: This is only useful in very special cases, like when generating proxy classes. + * + * @param string $className + * @param ClassMetadata $class + */ + public function setMetadataFor($className, $class) + { + $this->loadedMetadata[$className] = $class; + } + + /** + * Get array of parent classes for the given entity class + * + * @param string $name + * @return array $parentClasses + */ + protected function getParentClasses($name) + { + // Collect parent classes, ignoring transient (not-mapped) classes. + $parentClasses = array(); + foreach (array_reverse($this->getReflectionService()->getParentClasses($name)) as $parentClass) { + if ( ! $this->driver->isTransient($parentClass)) { + $parentClasses[] = $parentClass; + } + } + return $parentClasses; + } + + /** + * Loads the metadata of the class in question and all it's ancestors whose metadata + * is still not loaded. + * + * @param string $name The name of the class for which the metadata should get loaded. + * @param array $tables The metadata collection to which the loaded metadata is added. + */ + protected function loadMetadata($name) + { + if ( ! $this->initialized) { + $this->initialize(); + } + + $loaded = array(); + + $parentClasses = $this->getParentClasses($name); + $parentClasses[] = $name; + + // Move down the hierarchy of parent classes, starting from the topmost class + $parent = null; + $rootEntityFound = false; + $visited = array(); + foreach ($parentClasses as $className) { + if (isset($this->loadedMetadata[$className])) { + $parent = $this->loadedMetadata[$className]; + if ( ! $parent->isMappedSuperclass) { + $rootEntityFound = true; + array_unshift($visited, $className); + } + continue; + } + + $class = $this->newClassMetadataInstance($className); + $this->initializeReflection($class, $this->getReflectionService()); + + if ($parent) { + $class->setInheritanceType($parent->inheritanceType); + $class->setDiscriminatorColumn($parent->discriminatorColumn); + $class->setIdGeneratorType($parent->generatorType); + $this->addInheritedFields($class, $parent); + $this->addInheritedRelations($class, $parent); + $class->setIdentifier($parent->identifier); + $class->setVersioned($parent->isVersioned); + $class->setVersionField($parent->versionField); + $class->setDiscriminatorMap($parent->discriminatorMap); + $class->setLifecycleCallbacks($parent->lifecycleCallbacks); + $class->setChangeTrackingPolicy($parent->changeTrackingPolicy); + if ($parent->isMappedSuperclass) { + $class->setCustomRepositoryClass($parent->customRepositoryClassName); + } + } + + // Invoke driver + try { + $this->driver->loadMetadataForClass($className, $class); + } catch (ReflectionException $e) { + throw MappingException::reflectionFailure($className, $e); + } + + // If this class has a parent the id generator strategy is inherited. + // However this is only true if the hierachy of parents contains the root entity, + // if it consinsts of mapped superclasses these don't necessarily include the id field. + if ($parent && $rootEntityFound) { + if ($parent->isIdGeneratorSequence()) { + $class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition); + } else if ($parent->isIdGeneratorTable()) { + $class->getTableGeneratorDefinition($parent->tableGeneratorDefinition); + } + if ($parent->generatorType) { + $class->setIdGeneratorType($parent->generatorType); + } + if ($parent->idGenerator) { + $class->setIdGenerator($parent->idGenerator); + } + } else { + $this->completeIdGeneratorMapping($class); + } + + if ($parent && $parent->isInheritanceTypeSingleTable()) { + $class->setPrimaryTable($parent->table); + } + + if ($parent && $parent->containsForeignIdentifier) { + $class->containsForeignIdentifier = true; + } + + if ($parent && !empty ($parent->namedQueries)) { + $this->addInheritedNamedQueries($class, $parent); + } + + $class->setParentClasses($visited); + + if ($this->evm->hasListeners(Events::loadClassMetadata)) { + $eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class, $this->em); + $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); + } + $this->wakeupReflection($class, $this->getReflectionService()); + + $this->validateRuntimeMetadata($class, $parent); + + $this->loadedMetadata[$className] = $class; + + $parent = $class; + + if ( ! $class->isMappedSuperclass) { + $rootEntityFound = true; + array_unshift($visited, $className); + } + + $loaded[] = $className; + } + + return $loaded; + } + + /** + * Validate runtime metadata is correctly defined. + * + * @param ClassMetadata $class + * @param ClassMetadata $parent + */ + protected function validateRuntimeMetadata($class, $parent) + { + if ( ! $class->reflClass ) { + // only validate if there is a reflection class instance + return; + } + + $class->validateIdentifier(); + $class->validateAssocations(); + $class->validateLifecycleCallbacks($this->getReflectionService()); + + // verify inheritance + if (!$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) { + if (!$parent) { + if (count($class->discriminatorMap) == 0) { + throw MappingException::missingDiscriminatorMap($class->name); + } + if (!$class->discriminatorColumn) { + throw MappingException::missingDiscriminatorColumn($class->name); + } + } else if ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) { + // enforce discriminator map for all entities of an inheritance hierachy, otherwise problems will occur. + throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName); + } + } else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) { + // second condition is necessary for mapped superclasses in the middle of an inheritance hierachy + throw MappingException::noInheritanceOnMappedSuperClass($class->name); + } + } + + /** + * Creates a new ClassMetadata instance for the given class name. + * + * @param string $className + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + protected function newClassMetadataInstance($className) + { + return new ClassMetadata($className); + } + + /** + * Adds inherited fields to the subclass mapping. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + */ + private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->fieldMappings as $fieldName => $mapping) { + if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { + $mapping['inherited'] = $parentClass->name; + } + if ( ! isset($mapping['declared'])) { + $mapping['declared'] = $parentClass->name; + } + $subClass->addInheritedFieldMapping($mapping); + } + foreach ($parentClass->reflFields as $name => $field) { + $subClass->reflFields[$name] = $field; + } + } + + /** + * Adds inherited association mappings to the subclass mapping. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + */ + private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->associationMappings as $field => $mapping) { + if ($parentClass->isMappedSuperclass) { + if ($mapping['type'] & ClassMetadata::TO_MANY && !$mapping['isOwningSide']) { + throw MappingException::illegalToManyAssocationOnMappedSuperclass($parentClass->name, $field); + } + $mapping['sourceEntity'] = $subClass->name; + } + + //$subclassMapping = $mapping; + if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { + $mapping['inherited'] = $parentClass->name; + } + if ( ! isset($mapping['declared'])) { + $mapping['declared'] = $parentClass->name; + } + $subClass->addInheritedAssociationMapping($mapping); + } + } + + /** + * Adds inherited named queries to the subclass mapping. + * + * @since 2.2 + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + */ + private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->namedQueries as $name => $query) { + if (!isset ($subClass->namedQueries[$name])) { + $subClass->addNamedQuery(array( + 'name' => $query['name'], + 'query' => $query['query'] + )); + } + } + } + + /** + * Completes the ID generator mapping. If "auto" is specified we choose the generator + * most appropriate for the targeted database platform. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + */ + private function completeIdGeneratorMapping(ClassMetadataInfo $class) + { + $idGenType = $class->generatorType; + if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) { + if ($this->targetPlatform->prefersSequences()) { + $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE); + } else if ($this->targetPlatform->prefersIdentityColumns()) { + $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY); + } else { + $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_TABLE); + } + } + + // Create & assign an appropriate ID generator instance + switch ($class->generatorType) { + case ClassMetadata::GENERATOR_TYPE_IDENTITY: + // For PostgreSQL IDENTITY (SERIAL) we need a sequence name. It defaults to + // __seq in PostgreSQL for SERIAL columns. + // Not pretty but necessary and the simplest solution that currently works. + $seqName = $this->targetPlatform instanceof Platforms\PostgreSQLPlatform ? + $class->getTableName() . '_' . $class->columnNames[$class->identifier[0]] . '_seq' : + null; + $class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator($seqName)); + break; + case ClassMetadata::GENERATOR_TYPE_SEQUENCE: + // If there is no sequence definition yet, create a default definition + $definition = $class->sequenceGeneratorDefinition; + if ( ! $definition) { + $sequenceName = $class->getTableName() . '_' . $class->getSingleIdentifierColumnName() . '_seq'; + $definition['sequenceName'] = $this->targetPlatform->fixSchemaElementName($sequenceName); + $definition['allocationSize'] = 1; + $definition['initialValue'] = 1; + $class->setSequenceGeneratorDefinition($definition); + } + $sequenceGenerator = new \Doctrine\ORM\Id\SequenceGenerator( + $definition['sequenceName'], + $definition['allocationSize'] + ); + $class->setIdGenerator($sequenceGenerator); + break; + case ClassMetadata::GENERATOR_TYPE_NONE: + $class->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator()); + break; + case ClassMetadata::GENERATOR_TYPE_TABLE: + throw new ORMException("TableGenerator not yet implemented."); + break; + default: + throw new ORMException("Unknown generator type: " . $class->generatorType); + } + } + + /** + * Check if this class is mapped by this EntityManager + ClassMetadata configuration + * + * @param $class + * @return bool + */ + public function isTransient($class) + { + if ( ! $this->initialized) { + $this->initialize(); + } + + // Check for namespace alias + if (strpos($class, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $class); + $class = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + return $this->driver->isTransient($class); + } + + /** + * Get reflectionService. + * + * @return \Doctrine\Common\Persistence\Mapping\ReflectionService + */ + public function getReflectionService() + { + if ($this->reflectionService === null) { + $this->reflectionService = new RuntimeReflectionService(); + } + return $this->reflectionService; + } + + /** + * Set reflectionService. + * + * @param reflectionService the value to set. + */ + public function setReflectionService(ReflectionService $reflectionService) + { + $this->reflectionService = $reflectionService; + } + + /** + * Wakeup reflection after ClassMetadata gets unserialized from cache. + * + * @param ClassMetadataInfo $class + * @param ReflectionService $reflService + * @return void + */ + protected function wakeupReflection(ClassMetadataInfo $class, ReflectionService $reflService) + { + $class->wakeupReflection($reflService); + } + + /** + * Initialize Reflection after ClassMetadata was constructed. + * + * @param ClassMetadataInfo $class + * @param ReflectionService $reflService + * @return void + */ + protected function initializeReflection(ClassMetadataInfo $class, ReflectionService $reflService) + { + $class->initializeReflection($reflService); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php new file mode 100644 index 0000000..31e2b75 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -0,0 +1,2340 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\DBAL\Types\Type; +use ReflectionClass; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * A ClassMetadata instance holds all the object-relational mapping metadata + * of an entity and it's associations. + * + * Once populated, ClassMetadata instances are usually cached in a serialized form. + * + * IMPORTANT NOTE: + * + * The fields of this class are only public for 2 reasons: + * 1) To allow fast READ access. + * 2) To drastically reduce the size of a serialized instance (private/protected members + * get the whole class name, namespace inclusive, prepended to every property in + * the serialized representation). + * + * @author Roman Borschel + * @author Jonathan H. Wage + * @since 2.0 + */ +class ClassMetadataInfo implements ClassMetadata +{ + /* The inheritance mapping types */ + /** + * NONE means the class does not participate in an inheritance hierarchy + * and therefore does not need an inheritance mapping type. + */ + const INHERITANCE_TYPE_NONE = 1; + /** + * JOINED means the class will be persisted according to the rules of + * Class Table Inheritance. + */ + const INHERITANCE_TYPE_JOINED = 2; + /** + * SINGLE_TABLE means the class will be persisted according to the rules of + * Single Table Inheritance. + */ + const INHERITANCE_TYPE_SINGLE_TABLE = 3; + /** + * TABLE_PER_CLASS means the class will be persisted according to the rules + * of Concrete Table Inheritance. + */ + const INHERITANCE_TYPE_TABLE_PER_CLASS = 4; + + /* The Id generator types. */ + /** + * AUTO means the generator type will depend on what the used platform prefers. + * Offers full portability. + */ + const GENERATOR_TYPE_AUTO = 1; + /** + * SEQUENCE means a separate sequence object will be used. Platforms that do + * not have native sequence support may emulate it. Full portability is currently + * not guaranteed. + */ + const GENERATOR_TYPE_SEQUENCE = 2; + /** + * TABLE means a separate table is used for id generation. + * Offers full portability. + */ + const GENERATOR_TYPE_TABLE = 3; + /** + * IDENTITY means an identity column is used for id generation. The database + * will fill in the id column on insertion. Platforms that do not support + * native identity columns may emulate them. Full portability is currently + * not guaranteed. + */ + const GENERATOR_TYPE_IDENTITY = 4; + /** + * NONE means the class does not have a generated id. That means the class + * must have a natural, manually assigned id. + */ + const GENERATOR_TYPE_NONE = 5; + /** + * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time + * by doing a property-by-property comparison with the original data. This will + * be done for all entities that are in MANAGED state at commit-time. + * + * This is the default change tracking policy. + */ + const CHANGETRACKING_DEFERRED_IMPLICIT = 1; + /** + * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time + * by doing a property-by-property comparison with the original data. This will + * be done only for entities that were explicitly saved (through persist() or a cascade). + */ + const CHANGETRACKING_DEFERRED_EXPLICIT = 2; + /** + * NOTIFY means that Doctrine relies on the entities sending out notifications + * when their properties change. Such entity classes must implement + * the NotifyPropertyChanged interface. + */ + const CHANGETRACKING_NOTIFY = 3; + /** + * Specifies that an association is to be fetched when it is first accessed. + */ + const FETCH_LAZY = 2; + /** + * Specifies that an association is to be fetched when the owner of the + * association is fetched. + */ + const FETCH_EAGER = 3; + /** + * Specifies that an association is to be fetched lazy (on first access) and that + * commands such as Collection#count, Collection#slice are issued directly against + * the database if the collection is not yet initialized. + */ + const FETCH_EXTRA_LAZY = 4; + /** + * Identifies a one-to-one association. + */ + const ONE_TO_ONE = 1; + /** + * Identifies a many-to-one association. + */ + const MANY_TO_ONE = 2; + /** + * Identifies a one-to-many association. + */ + const ONE_TO_MANY = 4; + /** + * Identifies a many-to-many association. + */ + const MANY_TO_MANY = 8; + /** + * Combined bitmask for to-one (single-valued) associations. + */ + const TO_ONE = 3; + /** + * Combined bitmask for to-many (collection-valued) associations. + */ + const TO_MANY = 12; + + /** + * READ-ONLY: The name of the entity class. + */ + public $name; + + /** + * READ-ONLY: The namespace the entity class is contained in. + * + * @var string + * @todo Not really needed. Usage could be localized. + */ + public $namespace; + + /** + * READ-ONLY: The name of the entity class that is at the root of the mapped entity inheritance + * hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same + * as {@link $entityName}. + * + * @var string + */ + public $rootEntityName; + + /** + * The name of the custom repository class used for the entity class. + * (Optional). + * + * @var string + */ + public $customRepositoryClassName; + + /** + * READ-ONLY: Whether this class describes the mapping of a mapped superclass. + * + * @var boolean + */ + public $isMappedSuperclass = false; + + /** + * READ-ONLY: The names of the parent classes (ancestors). + * + * @var array + */ + public $parentClasses = array(); + + /** + * READ-ONLY: The names of all subclasses (descendants). + * + * @var array + */ + public $subClasses = array(); + + /** + * READ-ONLY: The named queries allowed to be called directly from Repository. + * + * @var array + */ + public $namedQueries = array(); + + /** + * READ-ONLY: The field names of all fields that are part of the identifier/primary key + * of the mapped entity class. + * + * @var array + */ + public $identifier = array(); + + /** + * READ-ONLY: The inheritance mapping type used by the class. + * + * @var integer + */ + public $inheritanceType = self::INHERITANCE_TYPE_NONE; + + /** + * READ-ONLY: The Id generator type used by the class. + * + * @var string + */ + public $generatorType = self::GENERATOR_TYPE_NONE; + + /** + * READ-ONLY: The field mappings of the class. + * Keys are field names and values are mapping definitions. + * + * The mapping definition array has the following values: + * + * - fieldName (string) + * The name of the field in the Entity. + * + * - type (string) + * The type name of the mapped field. Can be one of Doctrine's mapping types + * or a custom mapping type. + * + * - columnName (string, optional) + * The column name. Optional. Defaults to the field name. + * + * - length (integer, optional) + * The database length of the column. Optional. Default value taken from + * the type. + * + * - id (boolean, optional) + * Marks the field as the primary key of the entity. Multiple fields of an + * entity can have the id attribute, forming a composite key. + * + * - nullable (boolean, optional) + * Whether the column is nullable. Defaults to FALSE. + * + * - columnDefinition (string, optional, schema-only) + * The SQL fragment that is used when generating the DDL for the column. + * + * - precision (integer, optional, schema-only) + * The precision of a decimal column. Only valid if the column type is decimal. + * + * - scale (integer, optional, schema-only) + * The scale of a decimal column. Only valid if the column type is decimal. + * + [* - 'unique'] (string, optional, schema-only) + * Whether a unique constraint should be generated for the column. + * + * @var array + */ + public $fieldMappings = array(); + + /** + * READ-ONLY: An array of field names. Used to look up field names from column names. + * Keys are column names and values are field names. + * This is the reverse lookup map of $_columnNames. + * + * @var array + */ + public $fieldNames = array(); + + /** + * READ-ONLY: A map of field names to column names. Keys are field names and values column names. + * Used to look up column names from field names. + * This is the reverse lookup map of $_fieldNames. + * + * @var array + * @todo We could get rid of this array by just using $fieldMappings[$fieldName]['columnName']. + */ + public $columnNames = array(); + + /** + * READ-ONLY: The discriminator value of this class. + * + * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies + * where a discriminator column is used. + * + * @var mixed + * @see discriminatorColumn + */ + public $discriminatorValue; + + /** + * READ-ONLY: The discriminator map of all mapped classes in the hierarchy. + * + * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies + * where a discriminator column is used. + * + * @var mixed + * @see discriminatorColumn + */ + public $discriminatorMap = array(); + + /** + * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE + * inheritance mappings. + * + * @var array + */ + public $discriminatorColumn; + + /** + * READ-ONLY: The primary table definition. The definition is an array with the + * following entries: + * + * name => + * schema => + * indexes => array + * uniqueConstraints => array + * + * @var array + */ + public $table; + + /** + * READ-ONLY: The registered lifecycle callbacks for entities of this class. + * + * @var array + */ + public $lifecycleCallbacks = array(); + + /** + * READ-ONLY: The association mappings of this class. + * + * The mapping definition array supports the following keys: + * + * - fieldName (string) + * The name of the field in the entity the association is mapped to. + * + * - targetEntity (string) + * The class name of the target entity. If it is fully-qualified it is used as is. + * If it is a simple, unqualified class name the namespace is assumed to be the same + * as the namespace of the source entity. + * + * - mappedBy (string, required for bidirectional associations) + * The name of the field that completes the bidirectional association on the owning side. + * This key must be specified on the inverse side of a bidirectional association. + * + * - inversedBy (string, required for bidirectional associations) + * The name of the field that completes the bidirectional association on the inverse side. + * This key must be specified on the owning side of a bidirectional association. + * + * - cascade (array, optional) + * The names of persistence operations to cascade on the association. The set of possible + * values are: "persist", "remove", "detach", "merge", "refresh", "all" (implies all others). + * + * - orderBy (array, one-to-many/many-to-many only) + * A map of field names (of the target entity) to sorting directions (ASC/DESC). + * Example: array('priority' => 'desc') + * + * - fetch (integer, optional) + * The fetching strategy to use for the association, usually defaults to FETCH_LAZY. + * Possible values are: ClassMetadata::FETCH_EAGER, ClassMetadata::FETCH_LAZY. + * + * - joinTable (array, optional, many-to-many only) + * Specification of the join table and its join columns (foreign keys). + * Only valid for many-to-many mappings. Note that one-to-many associations can be mapped + * through a join table by simply mapping the association as many-to-many with a unique + * constraint on the join table. + * + * - indexBy (string, optional, to-many only) + * Specification of a field on target-entity that is used to index the collection by. + * This field HAS to be either the primary key or a unique column. Otherwise the collection + * does not contain all the entities that are actually related. + * + * A join table definition has the following structure: + *
    +     * array(
    +     *     'name' => ,
    +     *      'joinColumns' => array(),
    +     *      'inverseJoinColumns' => array()
    +     * )
    +     * 
    + * + * + * @var array + */ + public $associationMappings = array(); + + /** + * READ-ONLY: Flag indicating whether the identifier/primary key of the class is composite. + * + * @var boolean + */ + public $isIdentifierComposite = false; + + /** + * READ-ONLY: Flag indicating wheather the identifier/primary key contains at least one foreign key association. + * + * This flag is necessary because some code blocks require special treatment of this cases. + * + * @var boolean + */ + public $containsForeignIdentifier = false; + + /** + * READ-ONLY: The ID generator used for generating IDs for this class. + * + * @var AbstractIdGenerator + * @todo Remove! + */ + public $idGenerator; + + /** + * READ-ONLY: The definition of the sequence generator of this class. Only used for the + * SEQUENCE generation strategy. + * + * The definition has the following structure: + * + * array( + * 'sequenceName' => 'name', + * 'allocationSize' => 20, + * 'initialValue' => 1 + * ) + * + * + * @var array + * @todo Merge with tableGeneratorDefinition into generic generatorDefinition + */ + public $sequenceGeneratorDefinition; + + /** + * READ-ONLY: The definition of the table generator of this class. Only used for the + * TABLE generation strategy. + * + * @var array + * @todo Merge with tableGeneratorDefinition into generic generatorDefinition + */ + public $tableGeneratorDefinition; + + /** + * READ-ONLY: The policy used for change-tracking on entities of this class. + * + * @var integer + */ + public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT; + + /** + * READ-ONLY: A flag for whether or not instances of this class are to be versioned + * with optimistic locking. + * + * @var boolean $isVersioned + */ + public $isVersioned; + + /** + * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any). + * + * @var mixed $versionField + */ + public $versionField; + + /** + * The ReflectionClass instance of the mapped class. + * + * @var ReflectionClass + */ + public $reflClass; + + /** + * Is this entity marked as "read-only"? + * + * That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance + * optimization for entities that are immutable, either in your domain or through the relation database + * (coming from a view, or a history table for example). + * + * @var bool + */ + public $isReadOnly = false; + + /** + * The ReflectionProperty instances of the mapped class. + * + * @var array + */ + public $reflFields = array(); + + /** + * The prototype from which new instances of the mapped class are created. + * + * @var object + */ + private $_prototype; + + /** + * Initializes a new ClassMetadata instance that will hold the object-relational mapping + * metadata of the class with the given name. + * + * @param string $entityName The name of the entity class the new instance is used for. + */ + public function __construct($entityName) + { + $this->name = $entityName; + $this->rootEntityName = $entityName; + } + + /** + * Gets the ReflectionPropertys of the mapped class. + * + * @return array An array of ReflectionProperty instances. + */ + public function getReflectionProperties() + { + return $this->reflFields; + } + + /** + * Gets a ReflectionProperty for a specific field of the mapped class. + * + * @param string $name + * @return ReflectionProperty + */ + public function getReflectionProperty($name) + { + return $this->reflFields[$name]; + } + + /** + * Gets the ReflectionProperty for the single identifier field. + * + * @return ReflectionProperty + * @throws BadMethodCallException If the class has a composite identifier. + */ + public function getSingleIdReflectionProperty() + { + if ($this->isIdentifierComposite) { + throw new \BadMethodCallException("Class " . $this->name . " has a composite identifier."); + } + return $this->reflFields[$this->identifier[0]]; + } + + /** + * Extracts the identifier values of an entity of this class. + * + * For composite identifiers, the identifier values are returned as an array + * with the same order as the field order in {@link identifier}. + * + * @param object $entity + * @return array + */ + public function getIdentifierValues($entity) + { + if ($this->isIdentifierComposite) { + $id = array(); + + foreach ($this->identifier as $idField) { + $value = $this->reflFields[$idField]->getValue($entity); + + if ($value !== null) { + $id[$idField] = $value; + } + } + + return $id; + } + + $value = $this->reflFields[$this->identifier[0]]->getValue($entity); + + if ($value !== null) { + return array($this->identifier[0] => $value); + } + + return array(); + } + + /** + * Populates the entity identifier of an entity. + * + * @param object $entity + * @param mixed $id + * @todo Rename to assignIdentifier() + */ + public function setIdentifierValues($entity, array $id) + { + foreach ($id as $idField => $idValue) { + $this->reflFields[$idField]->setValue($entity, $idValue); + } + } + + /** + * Sets the specified field to the specified value on the given entity. + * + * @param object $entity + * @param string $field + * @param mixed $value + */ + public function setFieldValue($entity, $field, $value) + { + $this->reflFields[$field]->setValue($entity, $value); + } + + /** + * Gets the specified field's value off the given entity. + * + * @param object $entity + * @param string $field + */ + public function getFieldValue($entity, $field) + { + return $this->reflFields[$field]->getValue($entity); + } + + /** + * Creates a string representation of this instance. + * + * @return string The string representation of this instance. + * @todo Construct meaningful string representation. + */ + public function __toString() + { + return __CLASS__ . '@' . spl_object_hash($this); + } + + /** + * Determines which fields get serialized. + * + * It is only serialized what is necessary for best unserialization performance. + * That means any metadata properties that are not set or empty or simply have + * their default value are NOT serialized. + * + * Parts that are also NOT serialized because they can not be properly unserialized: + * - reflClass (ReflectionClass) + * - reflFields (ReflectionProperty array) + * + * @return array The names of all the fields that should be serialized. + */ + public function __sleep() + { + // This metadata is always serialized/cached. + $serialized = array( + 'associationMappings', + 'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName'] + 'fieldMappings', + 'fieldNames', + 'identifier', + 'isIdentifierComposite', // TODO: REMOVE + 'name', + 'namespace', // TODO: REMOVE + 'table', + 'rootEntityName', + 'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime. + ); + + // The rest of the metadata is only serialized if necessary. + if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) { + $serialized[] = 'changeTrackingPolicy'; + } + + if ($this->customRepositoryClassName) { + $serialized[] = 'customRepositoryClassName'; + } + + if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) { + $serialized[] = 'inheritanceType'; + $serialized[] = 'discriminatorColumn'; + $serialized[] = 'discriminatorValue'; + $serialized[] = 'discriminatorMap'; + $serialized[] = 'parentClasses'; + $serialized[] = 'subClasses'; + } + + if ($this->generatorType != self::GENERATOR_TYPE_NONE) { + $serialized[] = 'generatorType'; + if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) { + $serialized[] = 'sequenceGeneratorDefinition'; + } + } + + if ($this->isMappedSuperclass) { + $serialized[] = 'isMappedSuperclass'; + } + + if ($this->containsForeignIdentifier) { + $serialized[] = 'containsForeignIdentifier'; + } + + if ($this->isVersioned) { + $serialized[] = 'isVersioned'; + $serialized[] = 'versionField'; + } + + if ($this->lifecycleCallbacks) { + $serialized[] = 'lifecycleCallbacks'; + } + + if ($this->namedQueries) { + $serialized[] = 'namedQueries'; + } + + if ($this->isReadOnly) { + $serialized[] = 'isReadOnly'; + } + + return $serialized; + } + + /** + * Creates a new instance of the mapped class, without invoking the constructor. + * + * @return object + */ + public function newInstance() + { + if ($this->_prototype === null) { + $this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name)); + } + + return clone $this->_prototype; + } + /** + * Restores some state that can not be serialized/unserialized. + * + * @param ReflectionService $reflService + * @return void + */ + public function wakeupReflection($reflService) + { + // Restore ReflectionClass and properties + $this->reflClass = $reflService->getClass($this->name); + + foreach ($this->fieldMappings as $field => $mapping) { + $this->reflFields[$field] = isset($mapping['declared']) + ? $reflService->getAccessibleProperty($mapping['declared'], $field) + : $reflService->getAccessibleProperty($this->name, $field); + } + + foreach ($this->associationMappings as $field => $mapping) { + $this->reflFields[$field] = isset($mapping['declared']) + ? $reflService->getAccessibleProperty($mapping['declared'], $field) + : $reflService->getAccessibleProperty($this->name, $field); + } + } + + /** + * Initializes a new ClassMetadata instance that will hold the object-relational mapping + * metadata of the class with the given name. + * + * @param string $entityName The name of the entity class the new instance is used for. + */ + public function initializeReflection($reflService) + { + $this->reflClass = $reflService->getClass($this->name); + $this->namespace = $reflService->getClassNamespace($this->name); + $this->table['name'] = $reflService->getClassShortName($this->name); + + if ($this->reflClass) { + $this->name = $this->rootEntityName = $this->reflClass->getName(); + } + } + + /** + * Validate Identifier + * + * @return void + */ + public function validateIdentifier() + { + // Verify & complete identifier mapping + if ( ! $this->identifier && ! $this->isMappedSuperclass) { + throw MappingException::identifierRequired($this->name); + } + + if ($this->usesIdGenerator() && $this->isIdentifierComposite) { + throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name); + } + } + + /** + * Validate association targets actually exist. + * + * @return void + */ + public function validateAssocations() + { + foreach ($this->associationMappings as $field => $mapping) { + if ( ! \Doctrine\Common\ClassLoader::classExists($mapping['targetEntity']) ) { + throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']); + } + } + } + + /** + * Validate lifecycle callbacks + * + * @param ReflectionService $reflService + * @return void + */ + public function validateLifecycleCallbacks($reflService) + { + foreach ($this->lifecycleCallbacks as $event => $callbacks) { + foreach ($callbacks as $callbackFuncName) { + if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) { + throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName); + } + } + } + } + + /** + * Gets the ReflectionClass instance of the mapped class. + * + * @return ReflectionClass + */ + public function getReflectionClass() + { + return $this->reflClass; + } + + /** + * Sets the change tracking policy used by this class. + * + * @param integer $policy + */ + public function setChangeTrackingPolicy($policy) + { + $this->changeTrackingPolicy = $policy; + } + + /** + * Whether the change tracking policy of this class is "deferred explicit". + * + * @return boolean + */ + public function isChangeTrackingDeferredExplicit() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT; + } + + /** + * Whether the change tracking policy of this class is "deferred implicit". + * + * @return boolean + */ + public function isChangeTrackingDeferredImplicit() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT; + } + + /** + * Whether the change tracking policy of this class is "notify". + * + * @return boolean + */ + public function isChangeTrackingNotify() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY; + } + + /** + * Checks whether a field is part of the identifier/primary key field(s). + * + * @param string $fieldName The field name + * @return boolean TRUE if the field is part of the table identifier/primary key field(s), + * FALSE otherwise. + */ + public function isIdentifier($fieldName) + { + if ( ! $this->isIdentifierComposite) { + return $fieldName === $this->identifier[0]; + } + return in_array($fieldName, $this->identifier); + } + + /** + * Check if the field is unique. + * + * @param string $fieldName The field name + * @return boolean TRUE if the field is unique, FALSE otherwise. + */ + public function isUniqueField($fieldName) + { + $mapping = $this->getFieldMapping($fieldName); + if ($mapping !== false) { + return isset($mapping['unique']) && $mapping['unique'] == true; + } + return false; + } + + /** + * Check if the field is not null. + * + * @param string $fieldName The field name + * @return boolean TRUE if the field is not null, FALSE otherwise. + */ + public function isNullable($fieldName) + { + $mapping = $this->getFieldMapping($fieldName); + if ($mapping !== false) { + return isset($mapping['nullable']) && $mapping['nullable'] == true; + } + return false; + } + + /** + * Gets a column name for a field name. + * If the column name for the field cannot be found, the given field name + * is returned. + * + * @param string $fieldName The field name. + * @return string The column name. + */ + public function getColumnName($fieldName) + { + return isset($this->columnNames[$fieldName]) ? + $this->columnNames[$fieldName] : $fieldName; + } + + /** + * Gets the mapping of a (regular) field that holds some data but not a + * reference to another object. + * + * @param string $fieldName The field name. + * @return array The field mapping. + */ + public function getFieldMapping($fieldName) + { + if ( ! isset($this->fieldMappings[$fieldName])) { + throw MappingException::mappingNotFound($this->name, $fieldName); + } + return $this->fieldMappings[$fieldName]; + } + + /** + * Gets the mapping of an association. + * + * @see ClassMetadataInfo::$associationMappings + * @param string $fieldName The field name that represents the association in + * the object model. + * @return array The mapping. + */ + public function getAssociationMapping($fieldName) + { + if ( ! isset($this->associationMappings[$fieldName])) { + throw MappingException::mappingNotFound($this->name, $fieldName); + } + return $this->associationMappings[$fieldName]; + } + + /** + * Gets all association mappings of the class. + * + * @return array + */ + public function getAssociationMappings() + { + return $this->associationMappings; + } + + /** + * Gets the field name for a column name. + * If no field name can be found the column name is returned. + * + * @param string $columnName column name + * @return string column alias + */ + public function getFieldName($columnName) + { + return isset($this->fieldNames[$columnName]) ? + $this->fieldNames[$columnName] : $columnName; + } + + /** + * Gets the named query. + * + * @see ClassMetadataInfo::$namedQueries + * @throws MappingException + * @param string $queryName The query name + * @return string + */ + public function getNamedQuery($queryName) + { + if ( ! isset($this->namedQueries[$queryName])) { + throw MappingException::queryNotFound($this->name, $queryName); + } + return $this->namedQueries[$queryName]['dql']; + } + + /** + * Gets all named queries of the class. + * + * @return array + */ + public function getNamedQueries() + { + return $this->namedQueries; + } + + /** + * Validates & completes the given field mapping. + * + * @param array $mapping The field mapping to validated & complete. + * @return array The validated and completed field mapping. + */ + protected function _validateAndCompleteFieldMapping(array &$mapping) + { + // Check mandatory fields + if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) { + throw MappingException::missingFieldName($this->name); + } + if ( ! isset($mapping['type'])) { + // Default to string + $mapping['type'] = 'string'; + } + + // Complete fieldName and columnName mapping + if ( ! isset($mapping['columnName'])) { + $mapping['columnName'] = $mapping['fieldName']; + } else { + if ($mapping['columnName'][0] == '`') { + $mapping['columnName'] = trim($mapping['columnName'], '`'); + $mapping['quoted'] = true; + } + } + + $this->columnNames[$mapping['fieldName']] = $mapping['columnName']; + if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorColumn != null && $this->discriminatorColumn['name'] == $mapping['columnName'])) { + throw MappingException::duplicateColumnName($this->name, $mapping['columnName']); + } + + $this->fieldNames[$mapping['columnName']] = $mapping['fieldName']; + + // Complete id mapping + if (isset($mapping['id']) && $mapping['id'] === true) { + if ($this->versionField == $mapping['fieldName']) { + throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']); + } + + if ( ! in_array($mapping['fieldName'], $this->identifier)) { + $this->identifier[] = $mapping['fieldName']; + } + // Check for composite key + if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) { + $this->isIdentifierComposite = true; + } + } + + if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) { + if (isset($mapping['id']) && $mapping['id'] === true) { + throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']); + } + + $mapping['requireSQLConversion'] = true; + } + } + + /** + * Validates & completes the basic mapping information that is common to all + * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many). + * + * @param array $mapping The mapping. + * @return array The updated mapping. + * @throws MappingException If something is wrong with the mapping. + */ + protected function _validateAndCompleteAssociationMapping(array $mapping) + { + if ( ! isset($mapping['mappedBy'])) { + $mapping['mappedBy'] = null; + } + if ( ! isset($mapping['inversedBy'])) { + $mapping['inversedBy'] = null; + } + $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy + + // unset optional indexBy attribute if its empty + if (!isset($mapping['indexBy']) || !$mapping['indexBy']) { + unset($mapping['indexBy']); + } + + // If targetEntity is unqualified, assume it is in the same namespace as + // the sourceEntity. + $mapping['sourceEntity'] = $this->name; + + if (isset($mapping['targetEntity'])) { + if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) { + $mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity']; + } + + $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\'); + } + + if ( ($mapping['type'] & self::MANY_TO_ONE) > 0 && + isset($mapping['orphanRemoval']) && + $mapping['orphanRemoval'] == true) { + + throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']); + } + + // Complete id mapping + if (isset($mapping['id']) && $mapping['id'] === true) { + if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) { + throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']); + } + + if ( ! in_array($mapping['fieldName'], $this->identifier)) { + if (count($mapping['joinColumns']) >= 2) { + throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId( + $mapping['targetEntity'], $this->name, $mapping['fieldName'] + ); + } + + $this->identifier[] = $mapping['fieldName']; + $this->containsForeignIdentifier = true; + } + // Check for composite key + if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) { + $this->isIdentifierComposite = true; + } + } + + // Mandatory attributes for both sides + // Mandatory: fieldName, targetEntity + if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) { + throw MappingException::missingFieldName($this->name); + } + if ( ! isset($mapping['targetEntity'])) { + throw MappingException::missingTargetEntity($mapping['fieldName']); + } + + // Mandatory and optional attributes for either side + if ( ! $mapping['mappedBy']) { + if (isset($mapping['joinTable']) && $mapping['joinTable']) { + if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] == '`') { + $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`'); + $mapping['joinTable']['quoted'] = true; + } + } + } else { + $mapping['isOwningSide'] = false; + } + + if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) { + throw MappingException::illegalToManyIdentifierAssoaction($this->name, $mapping['fieldName']); + } + + // Fetch mode. Default fetch mode to LAZY, if not set. + if ( ! isset($mapping['fetch'])) { + $mapping['fetch'] = self::FETCH_LAZY; + } + + // Cascades + $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array(); + + if (in_array('all', $cascades)) { + $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach'); + } + + $mapping['cascade'] = $cascades; + $mapping['isCascadeRemove'] = in_array('remove', $cascades); + $mapping['isCascadePersist'] = in_array('persist', $cascades); + $mapping['isCascadeRefresh'] = in_array('refresh', $cascades); + $mapping['isCascadeMerge'] = in_array('merge', $cascades); + $mapping['isCascadeDetach'] = in_array('detach', $cascades); + + return $mapping; + } + + /** + * Validates & completes a one-to-one association mapping. + * + * @param array $mapping The mapping to validate & complete. + * @return array The validated & completed mapping. + * @override + */ + protected function _validateAndCompleteOneToOneMapping(array $mapping) + { + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + + if (isset($mapping['joinColumns']) && $mapping['joinColumns']) { + $mapping['isOwningSide'] = true; + } + + if ($mapping['isOwningSide']) { + if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) { + // Apply default join column + $mapping['joinColumns'] = array(array( + 'name' => $mapping['fieldName'] . '_id', + 'referencedColumnName' => 'id' + )); + } + + $uniqueContraintColumns = array(); + foreach ($mapping['joinColumns'] as $key => &$joinColumn) { + if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) { + if (count($mapping['joinColumns']) == 1) { + if (! isset($mapping['id']) || ! $mapping['id']) { + $joinColumn['unique'] = true; + } + } else { + $uniqueContraintColumns[] = $joinColumn['name']; + } + } + if (empty($joinColumn['name'])) { + $joinColumn['name'] = $mapping['fieldName'] . '_id'; + } + if (empty($joinColumn['referencedColumnName'])) { + $joinColumn['referencedColumnName'] = 'id'; + } + $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; + $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName']) + ? $joinColumn['fieldName'] : $joinColumn['name']; + } + + if ($uniqueContraintColumns) { + if (!$this->table) { + throw new \RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship."); + } + $this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array( + 'columns' => $uniqueContraintColumns + ); + } + + $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']); + } + + $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false; + $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove']; + + if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) { + throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']); + } + + return $mapping; + } + + /** + * Validates and completes the mapping. + * + * @param array $mapping The mapping to validate and complete. + * @return array The validated and completed mapping. + * @override + */ + protected function _validateAndCompleteOneToManyMapping(array $mapping) + { + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + + // OneToMany-side MUST be inverse (must have mappedBy) + if ( ! isset($mapping['mappedBy'])) { + throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']); + } + + $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false; + $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove']; + + if (isset($mapping['orderBy'])) { + if ( ! is_array($mapping['orderBy'])) { + throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy'])); + } + } + + return $mapping; + } + + protected function _validateAndCompleteManyToManyMapping(array $mapping) + { + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + if ($mapping['isOwningSide']) { + if (strpos($mapping['sourceEntity'], '\\') !== false) { + $sourceShortName = strtolower(substr($mapping['sourceEntity'], strrpos($mapping['sourceEntity'], '\\') + 1)); + } else { + $sourceShortName = strtolower($mapping['sourceEntity']); + } + if (strpos($mapping['targetEntity'], '\\') !== false) { + $targetShortName = strtolower(substr($mapping['targetEntity'], strrpos($mapping['targetEntity'], '\\') + 1)); + } else { + $targetShortName = strtolower($mapping['targetEntity']); + } + + // owning side MUST have a join table + if ( ! isset($mapping['joinTable']['name'])) { + $mapping['joinTable']['name'] = $sourceShortName .'_' . $targetShortName; + } + if ( ! isset($mapping['joinTable']['joinColumns'])) { + $mapping['joinTable']['joinColumns'] = array(array( + 'name' => $sourceShortName . '_id', + 'referencedColumnName' => 'id', + 'onDelete' => 'CASCADE')); + } + if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) { + $mapping['joinTable']['inverseJoinColumns'] = array(array( + 'name' => $targetShortName . '_id', + 'referencedColumnName' => 'id', + 'onDelete' => 'CASCADE')); + } + + foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) { + if (empty($joinColumn['name'])) { + $joinColumn['name'] = $sourceShortName . '_id'; + } + if (empty($joinColumn['referencedColumnName'])) { + $joinColumn['referencedColumnName'] = 'id'; + } + if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') { + $mapping['isOnDeleteCascade'] = true; + } + $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; + $mapping['joinTableColumns'][] = $joinColumn['name']; + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) { + if (empty($inverseJoinColumn['name'])) { + $inverseJoinColumn['name'] = $targetShortName . '_id'; + } + if (empty($inverseJoinColumn['referencedColumnName'])) { + $inverseJoinColumn['referencedColumnName'] = 'id'; + } + if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') { + $mapping['isOnDeleteCascade'] = true; + } + $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName']; + $mapping['joinTableColumns'][] = $inverseJoinColumn['name']; + } + } + + $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false; + + if (isset($mapping['orderBy'])) { + if ( ! is_array($mapping['orderBy'])) { + throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy'])); + } + } + + return $mapping; + } + + /** + * Gets the identifier (primary key) field names of the class. + * + * @return mixed + */ + public function getIdentifierFieldNames() + { + return $this->identifier; + } + + /** + * Gets the name of the single id field. Note that this only works on + * entity classes that have a single-field pk. + * + * @return string + * @throws MappingException If the class has a composite primary key. + */ + public function getSingleIdentifierFieldName() + { + if ($this->isIdentifierComposite) { + throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name); + } + return $this->identifier[0]; + } + + /** + * Gets the column name of the single id column. Note that this only works on + * entity classes that have a single-field pk. + * + * @return string + * @throws MappingException If the class has a composite primary key. + */ + public function getSingleIdentifierColumnName() + { + return $this->getColumnName($this->getSingleIdentifierFieldName()); + } + + /** + * INTERNAL: + * Sets the mapped identifier/primary key fields of this class. + * Mainly used by the ClassMetadataFactory to assign inherited identifiers. + * + * @param array $identifier + */ + public function setIdentifier(array $identifier) + { + $this->identifier = $identifier; + $this->isIdentifierComposite = (count($this->identifier) > 1); + } + + /** + * Gets the mapped identifier field of this class. + * + * @return string $identifier + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * Checks whether the class has a (mapped) field with a certain name. + * + * @return boolean + */ + public function hasField($fieldName) + { + return isset($this->fieldMappings[$fieldName]); + } + + /** + * Gets an array containing all the column names. + * + * @return array + */ + public function getColumnNames(array $fieldNames = null) + { + if ($fieldNames === null) { + return array_keys($this->fieldNames); + } else { + $columnNames = array(); + foreach ($fieldNames as $fieldName) { + $columnNames[] = $this->getColumnName($fieldName); + } + return $columnNames; + } + } + + /** + * Returns an array with all the identifier column names. + * + * @return array + */ + public function getIdentifierColumnNames() + { + $columnNames = array(); + + foreach ($this->identifier as $idProperty) { + if (isset($this->fieldMappings[$idProperty])) { + $columnNames[] = $this->fieldMappings[$idProperty]['columnName']; + + continue; + } + + // Association defined as Id field + $joinColumns = $this->associationMappings[$idProperty]['joinColumns']; + $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns); + + $columnNames = array_merge($columnNames, $assocColumnNames); + } + + return $columnNames; + } + + /** + * Sets the type of Id generator to use for the mapped class. + */ + public function setIdGeneratorType($generatorType) + { + $this->generatorType = $generatorType; + } + + /** + * Checks whether the mapped class uses an Id generator. + * + * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise. + */ + public function usesIdGenerator() + { + return $this->generatorType != self::GENERATOR_TYPE_NONE; + } + + /** + * @return boolean + */ + public function isInheritanceTypeNone() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_NONE; + } + + /** + * Checks whether the mapped class uses the JOINED inheritance mapping strategy. + * + * @return boolean TRUE if the class participates in a JOINED inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeJoined() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED; + } + + /** + * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy. + * + * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeSingleTable() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE; + } + + /** + * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy. + * + * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeTablePerClass() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS; + } + + /** + * Checks whether the class uses an identity column for the Id generation. + * + * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise. + */ + public function isIdGeneratorIdentity() + { + return $this->generatorType == self::GENERATOR_TYPE_IDENTITY; + } + + /** + * Checks whether the class uses a sequence for id generation. + * + * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise. + */ + public function isIdGeneratorSequence() + { + return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE; + } + + /** + * Checks whether the class uses a table for id generation. + * + * @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise. + */ + public function isIdGeneratorTable() + { + $this->generatorType == self::GENERATOR_TYPE_TABLE; + } + + /** + * Checks whether the class has a natural identifier/pk (which means it does + * not use any Id generator. + * + * @return boolean + */ + public function isIdentifierNatural() + { + return $this->generatorType == self::GENERATOR_TYPE_NONE; + } + + /** + * Gets the type of a field. + * + * @param string $fieldName + * @return \Doctrine\DBAL\Types\Type + */ + public function getTypeOfField($fieldName) + { + return isset($this->fieldMappings[$fieldName]) ? + $this->fieldMappings[$fieldName]['type'] : null; + } + + /** + * Gets the type of a column. + * + * @return \Doctrine\DBAL\Types\Type + */ + public function getTypeOfColumn($columnName) + { + return $this->getTypeOfField($this->getFieldName($columnName)); + } + + /** + * Gets the name of the primary table. + * + * @return string + */ + public function getTableName() + { + return $this->table['name']; + } + + /** + * Gets the table name to use for temporary identifier tables of this class. + * + * @return string + */ + public function getTemporaryIdTableName() + { + // replace dots with underscores because PostgreSQL creates temporary tables in a special schema + return str_replace('.', '_', $this->getTableName() . '_id_tmp'); + } + + /** + * Sets the mapped subclasses of this class. + * + * @param array $subclasses The names of all mapped subclasses. + */ + public function setSubclasses(array $subclasses) + { + foreach ($subclasses as $subclass) { + if (strpos($subclass, '\\') === false && strlen($this->namespace)) { + $this->subClasses[] = $this->namespace . '\\' . $subclass; + } else { + $this->subClasses[] = $subclass; + } + } + } + + /** + * Sets the parent class names. + * Assumes that the class names in the passed array are in the order: + * directParent -> directParentParent -> directParentParentParent ... -> root. + */ + public function setParentClasses(array $classNames) + { + $this->parentClasses = $classNames; + if (count($classNames) > 0) { + $this->rootEntityName = array_pop($classNames); + } + } + + /** + * Sets the inheritance type used by the class and it's subclasses. + * + * @param integer $type + */ + public function setInheritanceType($type) + { + if ( ! $this->_isInheritanceType($type)) { + throw MappingException::invalidInheritanceType($this->name, $type); + } + $this->inheritanceType = $type; + } + + /** + * Checks whether a mapped field is inherited from an entity superclass. + * + * @return boolean TRUE if the field is inherited, FALSE otherwise. + */ + public function isInheritedField($fieldName) + { + return isset($this->fieldMappings[$fieldName]['inherited']); + } + + /** + * Checks whether a mapped association field is inherited from a superclass. + * + * @param string $fieldName + * @return boolean TRUE if the field is inherited, FALSE otherwise. + */ + public function isInheritedAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]['inherited']); + } + + /** + * Sets the name of the primary table the class is mapped to. + * + * @param string $tableName The table name. + * @deprecated Use {@link setPrimaryTable}. + */ + public function setTableName($tableName) + { + $this->table['name'] = $tableName; + } + + /** + * Sets the primary table definition. The provided array supports the + * following structure: + * + * name => (optional, defaults to class name) + * indexes => array of indexes (optional) + * uniqueConstraints => array of constraints (optional) + * + * If a key is omitted, the current value is kept. + * + * @param array $table The table description. + */ + public function setPrimaryTable(array $table) + { + if (isset($table['name'])) { + if ($table['name'][0] == '`') { + $this->table['name'] = str_replace("`", "", $table['name']); + $this->table['quoted'] = true; + } else { + $this->table['name'] = $table['name']; + } + } + + if (isset($table['indexes'])) { + $this->table['indexes'] = $table['indexes']; + } + + if (isset($table['uniqueConstraints'])) { + $this->table['uniqueConstraints'] = $table['uniqueConstraints']; + } + } + + /** + * Checks whether the given type identifies an inheritance type. + * + * @param integer $type + * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise. + */ + private function _isInheritanceType($type) + { + return $type == self::INHERITANCE_TYPE_NONE || + $type == self::INHERITANCE_TYPE_SINGLE_TABLE || + $type == self::INHERITANCE_TYPE_JOINED || + $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS; + } + + /** + * Adds a mapped field to the class. + * + * @param array $mapping The field mapping. + */ + public function mapField(array $mapping) + { + $this->_validateAndCompleteFieldMapping($mapping); + if (isset($this->fieldMappings[$mapping['fieldName']]) || isset($this->associationMappings[$mapping['fieldName']])) { + throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']); + } + $this->fieldMappings[$mapping['fieldName']] = $mapping; + } + + /** + * INTERNAL: + * Adds an association mapping without completing/validating it. + * This is mainly used to add inherited association mappings to derived classes. + * + * @param array $mapping + */ + public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/) + { + if (isset($this->associationMappings[$mapping['fieldName']])) { + throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']); + } + $this->associationMappings[$mapping['fieldName']] = $mapping; + } + + /** + * INTERNAL: + * Adds a field mapping without completing/validating it. + * This is mainly used to add inherited field mappings to derived classes. + * + * @param array $mapping + */ + public function addInheritedFieldMapping(array $fieldMapping) + { + $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping; + $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName']; + $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName']; + } + + /** + * INTERNAL: + * Adds a named query to this class. + * + * @throws MappingException + * @param array $queryMapping + */ + public function addNamedQuery(array $queryMapping) + { + if (isset($this->namedQueries[$queryMapping['name']])) { + throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']); + } + + $name = $queryMapping['name']; + $query = $queryMapping['query']; + $dql = str_replace('__CLASS__', $this->name, $query); + $this->namedQueries[$name] = array( + 'name' => $name, + 'query' => $query, + 'dql' => $dql + ); + } + + /** + * Adds a one-to-one mapping. + * + * @param array $mapping The mapping. + */ + public function mapOneToOne(array $mapping) + { + $mapping['type'] = self::ONE_TO_ONE; + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + $this->_storeAssociationMapping($mapping); + } + + /** + * Adds a one-to-many mapping. + * + * @param array $mapping The mapping. + */ + public function mapOneToMany(array $mapping) + { + $mapping['type'] = self::ONE_TO_MANY; + $mapping = $this->_validateAndCompleteOneToManyMapping($mapping); + $this->_storeAssociationMapping($mapping); + } + + /** + * Adds a many-to-one mapping. + * + * @param array $mapping The mapping. + */ + public function mapManyToOne(array $mapping) + { + $mapping['type'] = self::MANY_TO_ONE; + // A many-to-one mapping is essentially a one-one backreference + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + $this->_storeAssociationMapping($mapping); + } + + /** + * Adds a many-to-many mapping. + * + * @param array $mapping The mapping. + */ + public function mapManyToMany(array $mapping) + { + $mapping['type'] = self::MANY_TO_MANY; + $mapping = $this->_validateAndCompleteManyToManyMapping($mapping); + $this->_storeAssociationMapping($mapping); + } + + /** + * Stores the association mapping. + * + * @param array $assocMapping + */ + protected function _storeAssociationMapping(array $assocMapping) + { + $sourceFieldName = $assocMapping['fieldName']; + + if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) { + throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName); + } + + $this->associationMappings[$sourceFieldName] = $assocMapping; + } + + /** + * Registers a custom repository class for the entity class. + * + * @param string $mapperClassName The class name of the custom mapper. + */ + public function setCustomRepositoryClass($repositoryClassName) + { + if ($repositoryClassName !== null && strpos($repositoryClassName, '\\') === false + && strlen($this->namespace) > 0) { + $repositoryClassName = $this->namespace . '\\' . $repositoryClassName; + } + $this->customRepositoryClassName = $repositoryClassName; + } + + /** + * Dispatches the lifecycle event of the given entity to the registered + * lifecycle callbacks and lifecycle listeners. + * + * @param string $event The lifecycle event. + * @param Entity $entity The Entity on which the event occured. + */ + public function invokeLifecycleCallbacks($lifecycleEvent, $entity) + { + foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) { + $entity->$callback(); + } + } + + /** + * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event. + * + * @param string $lifecycleEvent + * @return boolean + */ + public function hasLifecycleCallbacks($lifecycleEvent) + { + return isset($this->lifecycleCallbacks[$lifecycleEvent]); + } + + /** + * Gets the registered lifecycle callbacks for an event. + * + * @param string $event + * @return array + */ + public function getLifecycleCallbacks($event) + { + return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array(); + } + + /** + * Adds a lifecycle callback for entities of this class. + * + * @param string $callback + * @param string $event + */ + public function addLifecycleCallback($callback, $event) + { + $this->lifecycleCallbacks[$event][] = $callback; + } + + /** + * Sets the lifecycle callbacks for entities of this class. + * Any previously registered callbacks are overwritten. + * + * @param array $callbacks + */ + public function setLifecycleCallbacks(array $callbacks) + { + $this->lifecycleCallbacks = $callbacks; + } + + /** + * Sets the discriminator column definition. + * + * @param array $columnDef + * @see getDiscriminatorColumn() + */ + public function setDiscriminatorColumn($columnDef) + { + if ($columnDef !== null) { + if (isset($this->fieldNames[$columnDef['name']])) { + throw MappingException::duplicateColumnName($this->name, $columnDef['name']); + } + + if ( ! isset($columnDef['name'])) { + throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name, $columnDef); + } + if ( ! isset($columnDef['fieldName'])) { + $columnDef['fieldName'] = $columnDef['name']; + } + if ( ! isset($columnDef['type'])) { + $columnDef['type'] = "string"; + } + if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) { + throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']); + } + + $this->discriminatorColumn = $columnDef; + } + } + + /** + * Sets the discriminator values used by this class. + * Used for JOINED and SINGLE_TABLE inheritance mapping strategies. + * + * @param array $map + */ + public function setDiscriminatorMap(array $map) + { + foreach ($map as $value => $className) { + $this->addDiscriminatorMapClass($value, $className); + } + } + + /** + * Add one entry of the discriminator map with a new class and corresponding name. + * + * @param string $name + * @param string $className + */ + public function addDiscriminatorMapClass($name, $className) + { + if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) { + $className = $this->namespace . '\\' . $className; + } + + $className = ltrim($className, '\\'); + $this->discriminatorMap[$name] = $className; + + if ($this->name == $className) { + $this->discriminatorValue = $name; + } else { + if ( ! class_exists($className)) { + throw MappingException::invalidClassInDiscriminatorMap($className, $this->name); + } + if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) { + $this->subClasses[] = $className; + } + } + } + + /** + * Checks whether the class has a named query with the given query name. + * + * @param string $fieldName + * @return boolean + */ + public function hasNamedQuery($queryName) + { + return isset($this->namedQueries[$queryName]); + } + + /** + * Checks whether the class has a mapped association with the given field name. + * + * @param string $fieldName + * @return boolean + */ + public function hasAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]); + } + + /** + * Checks whether the class has a mapped association for the specified field + * and if yes, checks whether it is a single-valued association (to-one). + * + * @param string $fieldName + * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise. + */ + public function isSingleValuedAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]) && + ($this->associationMappings[$fieldName]['type'] & self::TO_ONE); + } + + /** + * Checks whether the class has a mapped association for the specified field + * and if yes, checks whether it is a collection-valued association (to-many). + * + * @param string $fieldName + * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise. + */ + public function isCollectionValuedAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]) && + ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE); + } + + /** + * Is this an association that only has a single join column? + * + * @param string $fieldName + * @return bool + */ + public function isAssociationWithSingleJoinColumn($fieldName) + { + return ( + isset($this->associationMappings[$fieldName]) && + isset($this->associationMappings[$fieldName]['joinColumns'][0]) && + !isset($this->associationMappings[$fieldName]['joinColumns'][1]) + ); + } + + /** + * Return the single association join column (if any). + * + * @param string $fieldName + * @return string + */ + public function getSingleAssociationJoinColumnName($fieldName) + { + if (!$this->isAssociationWithSingleJoinColumn($fieldName)) { + throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); + } + return $this->associationMappings[$fieldName]['joinColumns'][0]['name']; + } + + /** + * Return the single association referenced join column name (if any). + * + * @param string $fieldName + * @return string + */ + public function getSingleAssociationReferencedJoinColumnName($fieldName) + { + if (!$this->isAssociationWithSingleJoinColumn($fieldName)) { + throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); + } + return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName']; + } + + /** + * Used to retrieve a fieldname for either field or association from a given column, + * + * This method is used in foreign-key as primary-key contexts. + * + * @param string $columnName + * @return string + */ + public function getFieldForColumn($columnName) + { + if (isset($this->fieldNames[$columnName])) { + return $this->fieldNames[$columnName]; + } else { + foreach ($this->associationMappings AS $assocName => $mapping) { + if ($this->isAssociationWithSingleJoinColumn($assocName) && + $this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) { + + return $assocName; + } + } + + throw MappingException::noFieldNameFoundForColumn($this->name, $columnName); + } + } + + /** + * Sets the ID generator used to generate IDs for instances of this class. + * + * @param AbstractIdGenerator $generator + */ + public function setIdGenerator($generator) + { + $this->idGenerator = $generator; + } + + /** + * Sets the definition of the sequence ID generator for this class. + * + * The definition must have the following structure: + * + * array( + * 'sequenceName' => 'name', + * 'allocationSize' => 20, + * 'initialValue' => 1 + * ) + * + * + * @param array $definition + */ + public function setSequenceGeneratorDefinition(array $definition) + { + $this->sequenceGeneratorDefinition = $definition; + } + + /** + * Sets the version field mapping used for versioning. Sets the default + * value to use depending on the column type. + * + * @param array $mapping The version field mapping array + */ + public function setVersionMapping(array &$mapping) + { + $this->isVersioned = true; + $this->versionField = $mapping['fieldName']; + + if ( ! isset($mapping['default'])) { + if (in_array($mapping['type'], array('integer', 'bigint', 'smallint'))) { + $mapping['default'] = 1; + } else if ($mapping['type'] == 'datetime') { + $mapping['default'] = 'CURRENT_TIMESTAMP'; + } else { + throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']); + } + } + } + + /** + * Sets whether this class is to be versioned for optimistic locking. + * + * @param boolean $bool + */ + public function setVersioned($bool) + { + $this->isVersioned = $bool; + } + + /** + * Sets the name of the field that is to be used for versioning if this class is + * versioned for optimistic locking. + * + * @param string $versionField + */ + public function setVersionField($versionField) + { + $this->versionField = $versionField; + } + + /** + * Mark this class as read only, no change tracking is applied to it. + * + * @return void + */ + public function markReadOnly() + { + $this->isReadOnly = true; + } + + /** + * A numerically indexed list of field names of this persistent class. + * + * This array includes identifier fields if present on this class. + * + * @return array + */ + public function getFieldNames() + { + return array_keys($this->fieldMappings); + } + + /** + * A numerically indexed list of association names of this persistent class. + * + * This array includes identifier associations if present on this class. + * + * @return array + */ + public function getAssociationNames() + { + return array_keys($this->associationMappings); + } + + /** + * Returns the target class name of the given association. + * + * @param string $assocName + * @return string + */ + public function getAssociationTargetClass($assocName) + { + if ( ! isset($this->associationMappings[$assocName])) { + throw new \InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association."); + } + + return $this->associationMappings[$assocName]['targetEntity']; + } + + /** + * Get fully-qualified class name of this persistent class. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the (possibly quoted) identifier column names for safe use in an SQL statement. + * + * @param AbstractPlatform $platform + * @return array + */ + public function getQuotedIdentifierColumnNames($platform) + { + $quotedColumnNames = array(); + + foreach ($this->identifier as $idProperty) { + if (isset($this->fieldMappings[$idProperty])) { + $quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted']) + ? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName']) + : $this->fieldMappings[$idProperty]['columnName']; + + continue; + } + + // Association defined as Id field + $joinColumns = $this->associationMappings[$idProperty]['joinColumns']; + $assocQuotedColumnNames = array_map( + function ($joinColumn) { + return isset($joinColumn['quoted']) + ? $platform->quoteIdentifier($joinColumn['name']) + : $joinColumn['name']; + }, + $joinColumns + ); + + $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames); + } + + return $quotedColumnNames; + } + + /** + * Gets the (possibly quoted) column name of a mapped field for safe use + * in an SQL statement. + * + * @param string $field + * @param AbstractPlatform $platform + * @return string + */ + public function getQuotedColumnName($field, $platform) + { + return isset($this->fieldMappings[$field]['quoted']) + ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) + : $this->fieldMappings[$field]['columnName']; + } + + /** + * Gets the (possibly quoted) primary table name of this class for safe use + * in an SQL statement. + * + * @param AbstractPlatform $platform + * @return string + */ + public function getQuotedTableName($platform) + { + return isset($this->table['quoted']) ? $platform->quoteIdentifier($this->table['name']) : $this->table['name']; + } + + /** + * Gets the (possibly quoted) name of the join table. + * + * @param AbstractPlatform $platform + * @return string + */ + public function getQuotedJoinTableName(array $assoc, $platform) + { + return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name']; + } + + /** + * @param string $fieldName + * @return bool + */ + public function isAssociationInverseSide($fieldName) + { + return isset($this->associationMappings[$fieldName]) && ! $this->associationMappings[$fieldName]['isOwningSide']; + } + + /** + * @param string $fieldName + * @return string + */ + public function getAssociationMappedByTargetField($fieldName) + { + return $this->associationMappings[$fieldName]['mappedBy']; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Column.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Column.php new file mode 100644 index 0000000..5f7dd7f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Column.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class Column implements Annotation +{ + /** @var string */ + public $name; + /** @var mixed */ + public $type = 'string'; + /** @var integer */ + public $length; + /** @var integer */ + public $precision = 0; // The precision for a decimal (exact numeric) column (Applies only for decimal column) + /** @var integer */ + public $scale = 0; // The scale for a decimal (exact numeric) column (Applies only for decimal column) + /** @var boolean */ + public $unique = false; + /** @var boolean */ + public $nullable = false; + /** @var array */ + public $options = array(); + /** @var string */ + public $columnDefinition; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php new file mode 100644 index 0000000..aec0115 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class DiscriminatorColumn implements Annotation +{ + /** @var string */ + public $name; + /** @var string */ + public $type; + /** @var integer */ + public $length; + /** @var mixed */ + public $fieldName; // field name used in non-object hydration (array/scalar) +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorMap.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorMap.php new file mode 100644 index 0000000..8505cf9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorMap.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class DiscriminatorMap implements Annotation +{ + /** @var array */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php new file mode 100644 index 0000000..6953bf2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php @@ -0,0 +1,213 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\ORM\Mapping\MappingException; + +/** + * Base driver for file-based metadata drivers. + * + * A file driver operates in a mode where it loads the mapping files of individual + * classes on demand. This requires the user to adhere to the convention of 1 mapping + * file per class and the file names of the mapping files must correspond to the full + * class name, including namespace, with the namespace delimiters '\', replaced by dots '.'. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +abstract class AbstractFileDriver implements Driver +{ + /** + * The paths where to look for mapping files. + * + * @var array + */ + protected $_paths = array(); + + /** + * The file extension of mapping documents. + * + * @var string + */ + protected $_fileExtension; + + /** + * Initializes a new FileDriver that looks in the given path(s) for mapping + * documents and operates in the specified operating mode. + * + * @param string|array $paths One or multiple paths where mapping documents can be found. + */ + public function __construct($paths) + { + $this->addPaths((array) $paths); + } + + /** + * Append lookup paths to metadata driver. + * + * @param array $paths + */ + public function addPaths(array $paths) + { + $this->_paths = array_unique(array_merge($this->_paths, $paths)); + } + + /** + * Retrieve the defined metadata lookup paths. + * + * @return array + */ + public function getPaths() + { + return $this->_paths; + } + + /** + * Get the file extension used to look for mapping files under + * + * @return void + */ + public function getFileExtension() + { + return $this->_fileExtension; + } + + /** + * Set the file extension used to look for mapping files under + * + * @param string $fileExtension The file extension to set + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->_fileExtension = $fileExtension; + } + + /** + * Get the element of schema meta data for the class from the mapping file. + * This will lazily load the mapping file if it is not loaded yet + * + * @return array $element The element of schema meta data + */ + public function getElement($className) + { + $result = $this->_loadMappingFile($this->_findMappingFile($className)); + + if(!isset($result[$className])){ + throw MappingException::invalidMappingFile($className, str_replace('\\', '.', $className) . $this->_fileExtension); + } + return $result[$className]; + } + + /** + * Whether the class with the specified name should have its metadata loaded. + * This is only the case if it is either mapped as an Entity or a + * MappedSuperclass. + * + * @param string $className + * @return boolean + */ + public function isTransient($className) + { + $fileName = str_replace('\\', '.', $className) . $this->_fileExtension; + + // Check whether file exists + foreach ((array) $this->_paths as $path) { + if (file_exists($path . DIRECTORY_SEPARATOR . $fileName)) { + return false; + } + } + + return true; + } + + /** + * Gets the names of all mapped classes known to this driver. + * + * @return array The names of all mapped classes known to this driver. + */ + public function getAllClassNames() + { + $classes = array(); + + if ($this->_paths) { + foreach ((array) $this->_paths as $path) { + if ( ! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + if (($fileName = $file->getBasename($this->_fileExtension)) == $file->getBasename()) { + continue; + } + + // NOTE: All files found here means classes are not transient! + $classes[] = str_replace('.', '\\', $fileName); + } + } + } + + return $classes; + } + + /** + * Finds the mapping file for the class with the given name by searching + * through the configured paths. + * + * @param $className + * @return string The (absolute) file name. + * @throws MappingException + */ + protected function _findMappingFile($className) + { + $fileName = str_replace('\\', '.', $className) . $this->_fileExtension; + + // Check whether file exists + foreach ((array) $this->_paths as $path) { + if (file_exists($path . DIRECTORY_SEPARATOR . $fileName)) { + return $path . DIRECTORY_SEPARATOR . $fileName; + } + } + + throw MappingException::mappingFileNotFound($className, $fileName); + } + + /** + * Loads a mapping file with the given name and returns a map + * from class/entity names to their corresponding elements. + * + * @param string $file The mapping file to load. + * @return array + */ + abstract protected function _loadMappingFile($file); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php new file mode 100644 index 0000000..ace39c4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -0,0 +1,592 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Cache\ArrayCache, + Doctrine\Common\Annotations\AnnotationReader, + Doctrine\Common\Annotations\AnnotationRegistry, + Doctrine\ORM\Mapping\ClassMetadataInfo, + Doctrine\ORM\Mapping\MappingException; + +/** + * The AnnotationDriver reads the mapping metadata from docblock annotations. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class AnnotationDriver implements Driver +{ + /** + * The AnnotationReader. + * + * @var AnnotationReader + */ + protected $_reader; + + /** + * The paths where to look for mapping files. + * + * @var array + */ + protected $_paths = array(); + + /** + * The file extension of mapping documents. + * + * @var string + */ + protected $_fileExtension = '.php'; + + /** + * @param array + */ + protected $_classNames; + + /** + * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading + * docblock annotations. + * + * @param AnnotationReader $reader The AnnotationReader to use, duck-typed. + * @param string|array $paths One or multiple paths where mapping classes can be found. + */ + public function __construct($reader, $paths = null) + { + $this->_reader = $reader; + if ($paths) { + $this->addPaths((array) $paths); + } + } + + /** + * Append lookup paths to metadata driver. + * + * @param array $paths + */ + public function addPaths(array $paths) + { + $this->_paths = array_unique(array_merge($this->_paths, $paths)); + } + + /** + * Retrieve the defined metadata lookup paths. + * + * @return array + */ + public function getPaths() + { + return $this->_paths; + } + + /** + * Retrieve the current annotation reader + * + * @return AnnotationReader + */ + public function getReader() + { + return $this->_reader; + } + + /** + * Get the file extension used to look for mapping files under + * + * @return void + */ + public function getFileExtension() + { + return $this->_fileExtension; + } + + /** + * Set the file extension used to look for mapping files under + * + * @param string $fileExtension The file extension to set + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->_fileExtension = $fileExtension; + } + + /** + * {@inheritdoc} + */ + public function loadMetadataForClass($className, ClassMetadataInfo $metadata) + { + $class = $metadata->getReflectionClass(); + if (!$class) { + // this happens when running annotation driver in combination with + // static reflection services. This is not the nicest fix + $class = new \ReflectionClass($metadata->name); + } + + $classAnnotations = $this->_reader->getClassAnnotations($class); + + if ($classAnnotations && is_numeric(key($classAnnotations))) { + foreach ($classAnnotations as $annot) { + $classAnnotations[get_class($annot)] = $annot; + } + } + + // Evaluate Entity annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) { + $entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity']; + if ($entityAnnot->repositoryClass !== null) { + $metadata->setCustomRepositoryClass($entityAnnot->repositoryClass); + } + if ($entityAnnot->readOnly) { + $metadata->markReadOnly(); + } + } else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) { + $mappedSuperclassAnnot = $classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass']; + $metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass); + $metadata->isMappedSuperclass = true; + } else { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); + } + + // Evaluate Table annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\Table'])) { + $tableAnnot = $classAnnotations['Doctrine\ORM\Mapping\Table']; + $primaryTable = array( + 'name' => $tableAnnot->name, + 'schema' => $tableAnnot->schema + ); + + if ($tableAnnot->indexes !== null) { + foreach ($tableAnnot->indexes as $indexAnnot) { + $index = array('columns' => $indexAnnot->columns); + + if ( ! empty($indexAnnot->name)) { + $primaryTable['indexes'][$indexAnnot->name] = $index; + } else { + $primaryTable['indexes'][] = $index; + } + } + } + + if ($tableAnnot->uniqueConstraints !== null) { + foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) { + $uniqueConstraint = array('columns' => $uniqueConstraintAnnot->columns); + + if ( ! empty($uniqueConstraintAnnot->name)) { + $primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint; + } else { + $primaryTable['uniqueConstraints'][] = $uniqueConstraint; + } + } + } + + $metadata->setPrimaryTable($primaryTable); + } + + // Evaluate NamedQueries annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedQueries'])) { + $namedQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedQueries']; + + if (!is_array($namedQueriesAnnot->value)) { + throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations."); + } + + foreach ($namedQueriesAnnot->value as $namedQuery) { + if (!($namedQuery instanceof \Doctrine\ORM\Mapping\NamedQuery)) { + throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations."); + } + $metadata->addNamedQuery(array( + 'name' => $namedQuery->name, + 'query' => $namedQuery->query + )); + } + } + + // Evaluate InheritanceType annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\InheritanceType'])) { + $inheritanceTypeAnnot = $classAnnotations['Doctrine\ORM\Mapping\InheritanceType']; + $metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value)); + + if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) { + // Evaluate DiscriminatorColumn annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn'])) { + $discrColumnAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn']; + $metadata->setDiscriminatorColumn(array( + 'name' => $discrColumnAnnot->name, + 'type' => $discrColumnAnnot->type, + 'length' => $discrColumnAnnot->length + )); + } else { + $metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255)); + } + + // Evaluate DiscriminatorMap annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'])) { + $discrMapAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap']; + $metadata->setDiscriminatorMap($discrMapAnnot->value); + } + } + } + + + // Evaluate DoctrineChangeTrackingPolicy annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy'])) { + $changeTrackingAnnot = $classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy']; + $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' . $changeTrackingAnnot->value)); + } + + // Evaluate annotations on properties/fields + foreach ($class->getProperties() as $property) { + if ($metadata->isMappedSuperclass && ! $property->isPrivate() + || + $metadata->isInheritedField($property->name) + || + $metadata->isInheritedAssociation($property->name)) { + continue; + } + + $mapping = array(); + $mapping['fieldName'] = $property->getName(); + + // Check for JoinColummn/JoinColumns annotations + $joinColumns = array(); + + if ($joinColumnAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumn')) { + $joinColumns[] = array( + 'name' => $joinColumnAnnot->name, + 'referencedColumnName' => $joinColumnAnnot->referencedColumnName, + 'unique' => $joinColumnAnnot->unique, + 'nullable' => $joinColumnAnnot->nullable, + 'onDelete' => $joinColumnAnnot->onDelete, + 'columnDefinition' => $joinColumnAnnot->columnDefinition, + ); + } else if ($joinColumnsAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumns')) { + foreach ($joinColumnsAnnot->value as $joinColumn) { + $joinColumns[] = array( + 'name' => $joinColumn->name, + 'referencedColumnName' => $joinColumn->referencedColumnName, + 'unique' => $joinColumn->unique, + 'nullable' => $joinColumn->nullable, + 'onDelete' => $joinColumn->onDelete, + 'columnDefinition' => $joinColumn->columnDefinition, + ); + } + } + + // Field can only be annotated with one of: + // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany + if ($columnAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Column')) { + if ($columnAnnot->type == null) { + throw MappingException::propertyTypeIsRequired($className, $property->getName()); + } + + $mapping['type'] = $columnAnnot->type; + $mapping['length'] = $columnAnnot->length; + $mapping['precision'] = $columnAnnot->precision; + $mapping['scale'] = $columnAnnot->scale; + $mapping['nullable'] = $columnAnnot->nullable; + $mapping['unique'] = $columnAnnot->unique; + if ($columnAnnot->options) { + $mapping['options'] = $columnAnnot->options; + } + + if (isset($columnAnnot->name)) { + $mapping['columnName'] = $columnAnnot->name; + } + + if (isset($columnAnnot->columnDefinition)) { + $mapping['columnDefinition'] = $columnAnnot->columnDefinition; + } + + if ($idAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) { + $mapping['id'] = true; + } + + if ($generatedValueAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\GeneratedValue')) { + $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy)); + } + + if ($versionAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Version')) { + $metadata->setVersionMapping($mapping); + } + + $metadata->mapField($mapping); + + // Check for SequenceGenerator/TableGenerator definition + if ($seqGeneratorAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\SequenceGenerator')) { + $metadata->setSequenceGeneratorDefinition(array( + 'sequenceName' => $seqGeneratorAnnot->sequenceName, + 'allocationSize' => $seqGeneratorAnnot->allocationSize, + 'initialValue' => $seqGeneratorAnnot->initialValue + )); + } else if ($tblGeneratorAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) { + throw MappingException::tableIdGeneratorNotImplemented($className); + } + } else if ($oneToOneAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToOne')) { + if ($idAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) { + $mapping['id'] = true; + } + + $mapping['targetEntity'] = $oneToOneAnnot->targetEntity; + $mapping['joinColumns'] = $joinColumns; + $mapping['mappedBy'] = $oneToOneAnnot->mappedBy; + $mapping['inversedBy'] = $oneToOneAnnot->inversedBy; + $mapping['cascade'] = $oneToOneAnnot->cascade; + $mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval; + $mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnot->fetch); + $metadata->mapOneToOne($mapping); + } else if ($oneToManyAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToMany')) { + $mapping['mappedBy'] = $oneToManyAnnot->mappedBy; + $mapping['targetEntity'] = $oneToManyAnnot->targetEntity; + $mapping['cascade'] = $oneToManyAnnot->cascade; + $mapping['indexBy'] = $oneToManyAnnot->indexBy; + $mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval; + $mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnot->fetch); + + if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) { + $mapping['orderBy'] = $orderByAnnot->value; + } + + $metadata->mapOneToMany($mapping); + } else if ($manyToOneAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToOne')) { + if ($idAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) { + $mapping['id'] = true; + } + + $mapping['joinColumns'] = $joinColumns; + $mapping['cascade'] = $manyToOneAnnot->cascade; + $mapping['inversedBy'] = $manyToOneAnnot->inversedBy; + $mapping['targetEntity'] = $manyToOneAnnot->targetEntity; + $mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnot->fetch); + $metadata->mapManyToOne($mapping); + } else if ($manyToManyAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToMany')) { + $joinTable = array(); + + if ($joinTableAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinTable')) { + $joinTable = array( + 'name' => $joinTableAnnot->name, + 'schema' => $joinTableAnnot->schema + ); + + foreach ($joinTableAnnot->joinColumns as $joinColumn) { + $joinTable['joinColumns'][] = array( + 'name' => $joinColumn->name, + 'referencedColumnName' => $joinColumn->referencedColumnName, + 'unique' => $joinColumn->unique, + 'nullable' => $joinColumn->nullable, + 'onDelete' => $joinColumn->onDelete, + 'columnDefinition' => $joinColumn->columnDefinition, + ); + } + + foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) { + $joinTable['inverseJoinColumns'][] = array( + 'name' => $joinColumn->name, + 'referencedColumnName' => $joinColumn->referencedColumnName, + 'unique' => $joinColumn->unique, + 'nullable' => $joinColumn->nullable, + 'onDelete' => $joinColumn->onDelete, + 'columnDefinition' => $joinColumn->columnDefinition, + ); + } + } + + $mapping['joinTable'] = $joinTable; + $mapping['targetEntity'] = $manyToManyAnnot->targetEntity; + $mapping['mappedBy'] = $manyToManyAnnot->mappedBy; + $mapping['inversedBy'] = $manyToManyAnnot->inversedBy; + $mapping['cascade'] = $manyToManyAnnot->cascade; + $mapping['indexBy'] = $manyToManyAnnot->indexBy; + $mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval; + $mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch); + + if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) { + $mapping['orderBy'] = $orderByAnnot->value; + } + + $metadata->mapManyToMany($mapping); + } + } + + // Evaluate @HasLifecycleCallbacks annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) { + foreach ($class->getMethods() as $method) { + // filter for the declaring class only, callbacks from parents will already be registered. + if ($method->isPublic() && $method->getDeclaringClass()->getName() == $class->name) { + $annotations = $this->_reader->getMethodAnnotations($method); + + if ($annotations && is_numeric(key($annotations))) { + foreach ($annotations as $annot) { + $annotations[get_class($annot)] = $annot; + } + } + + if (isset($annotations['Doctrine\ORM\Mapping\PrePersist'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::prePersist); + } + + if (isset($annotations['Doctrine\ORM\Mapping\PostPersist'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postPersist); + } + + if (isset($annotations['Doctrine\ORM\Mapping\PreUpdate'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preUpdate); + } + + if (isset($annotations['Doctrine\ORM\Mapping\PostUpdate'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postUpdate); + } + + if (isset($annotations['Doctrine\ORM\Mapping\PreRemove'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preRemove); + } + + if (isset($annotations['Doctrine\ORM\Mapping\PostRemove'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postRemove); + } + + if (isset($annotations['Doctrine\ORM\Mapping\PostLoad'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postLoad); + } + + if (isset($annotations['Doctrine\ORM\Mapping\PreFlush'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preFlush); + } + } + } + } + } + + /** + * Whether the class with the specified name is transient. Only non-transient + * classes, that is entities and mapped superclasses, should have their metadata loaded. + * A class is non-transient if it is annotated with either @Entity or + * @MappedSuperclass in the class doc block. + * + * @param string $className + * @return boolean + */ + public function isTransient($className) + { + $classAnnotations = $this->_reader->getClassAnnotations(new \ReflectionClass($className)); + + if ($classAnnotations && is_numeric(key($classAnnotations))) { + foreach ($classAnnotations as $annot) { + if ($annot instanceof \Doctrine\ORM\Mapping\Entity) { + return false; + } + if ($annot instanceof \Doctrine\ORM\Mapping\MappedSuperclass) { + return false; + } + } + + return true; + } + + return ! isset($classAnnotations['Doctrine\ORM\Mapping\Entity']) && + ! isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass']); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + if ($this->_classNames !== null) { + return $this->_classNames; + } + + if (!$this->_paths) { + throw MappingException::pathRequired(); + } + + $classes = array(); + $includedFiles = array(); + + foreach ($this->_paths as $path) { + if ( ! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RegexIterator( + new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::LEAVES_ONLY + ), + '/^.+' . str_replace('.', '\.', $this->_fileExtension) . '$/i', + \RecursiveRegexIterator::GET_MATCH + ); + + foreach ($iterator as $file) { + $sourceFile = realpath($file[0]); + + require_once $sourceFile; + + $includedFiles[] = $sourceFile; + } + } + + $declared = get_declared_classes(); + + foreach ($declared as $className) { + $rc = new \ReflectionClass($className); + $sourceFile = $rc->getFileName(); + if (in_array($sourceFile, $includedFiles) && ! $this->isTransient($className)) { + $classes[] = $className; + } + } + + $this->_classNames = $classes; + + return $classes; + } + + /** + * Attempts to resolve the fetch mode. + * + * @param string $className The class name + * @param string $fetchMode The fetch mode + * @return integer The fetch mode as defined in ClassMetadata + * @throws MappingException If the fetch mode is not valid + */ + private function getFetchMode($className, $fetchMode) + { + if(!defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) { + throw MappingException::invalidFetchMode($className, $fetchMode); + } + + return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode); + } + /** + * Factory method for the Annotation Driver + * + * @param array|string $paths + * @param AnnotationReader $reader + * @return AnnotationDriver + */ + static public function create($paths = array(), AnnotationReader $reader = null) + { + if ($reader == null) { + $reader = new AnnotationReader(); + $reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\'); + } + return new self($reader, $paths); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php new file mode 100644 index 0000000..bcbdbd5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php @@ -0,0 +1,417 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Cache\ArrayCache, + Doctrine\Common\Annotations\AnnotationReader, + Doctrine\DBAL\Schema\AbstractSchemaManager, + Doctrine\DBAL\Schema\SchemaException, + Doctrine\ORM\Mapping\ClassMetadataInfo, + Doctrine\ORM\Mapping\MappingException, + Doctrine\Common\Util\Inflector; + +/** + * The DatabaseDriver reverse engineers the mapping metadata from a database. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Benjamin Eberlei + */ +class DatabaseDriver implements Driver +{ + /** + * @var AbstractSchemaManager + */ + private $_sm; + + /** + * @var array + */ + private $tables = null; + + private $classToTableNames = array(); + + /** + * @var array + */ + private $manyToManyTables = array(); + + /** + * @var array + */ + private $classNamesForTables = array(); + + /** + * @var array + */ + private $fieldNamesForColumns = array(); + + /** + * The namespace for the generated entities. + * + * @var string + */ + private $namespace; + + /** + * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading + * docblock annotations. + * + * @param AnnotationReader $reader The AnnotationReader to use. + */ + public function __construct(AbstractSchemaManager $schemaManager) + { + $this->_sm = $schemaManager; + } + + /** + * Set tables manually instead of relying on the reverse engeneering capabilities of SchemaManager. + * + * @param array $entityTables + * @param array $manyToManyTables + * @return void + */ + public function setTables($entityTables, $manyToManyTables) + { + $this->tables = $this->manyToManyTables = $this->classToTableNames = array(); + foreach ($entityTables AS $table) { + $className = $this->getClassNameForTable($table->getName()); + $this->classToTableNames[$className] = $table->getName(); + $this->tables[$table->getName()] = $table; + } + foreach ($manyToManyTables AS $table) { + $this->manyToManyTables[$table->getName()] = $table; + } + } + + private function reverseEngineerMappingFromDatabase() + { + if ($this->tables !== null) { + return; + } + + $tables = array(); + + foreach ($this->_sm->listTableNames() as $tableName) { + $tables[$tableName] = $this->_sm->listTableDetails($tableName); + } + + $this->tables = $this->manyToManyTables = $this->classToTableNames = array(); + foreach ($tables AS $tableName => $table) { + /* @var $table Table */ + if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) { + $foreignKeys = $table->getForeignKeys(); + } else { + $foreignKeys = array(); + } + + $allForeignKeyColumns = array(); + foreach ($foreignKeys AS $foreignKey) { + $allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns()); + } + + if ( ! $table->hasPrimaryKey()) { + throw new MappingException( + "Table " . $table->getName() . " has no primary key. Doctrine does not ". + "support reverse engineering from tables that don't have a primary key." + ); + } + + $pkColumns = $table->getPrimaryKey()->getColumns(); + sort($pkColumns); + sort($allForeignKeyColumns); + + if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) { + $this->manyToManyTables[$tableName] = $table; + } else { + // lower-casing is necessary because of Oracle Uppercase Tablenames, + // assumption is lower-case + underscore separated. + $className = $this->getClassNameForTable($tableName); + $this->tables[$tableName] = $table; + $this->classToTableNames[$className] = $tableName; + } + } + } + + /** + * {@inheritdoc} + */ + public function loadMetadataForClass($className, ClassMetadataInfo $metadata) + { + $this->reverseEngineerMappingFromDatabase(); + + if (!isset($this->classToTableNames[$className])) { + throw new \InvalidArgumentException("Unknown class " . $className); + } + + $tableName = $this->classToTableNames[$className]; + + $metadata->name = $className; + $metadata->table['name'] = $tableName; + + $columns = $this->tables[$tableName]->getColumns(); + $indexes = $this->tables[$tableName]->getIndexes(); + try { + $primaryKeyColumns = $this->tables[$tableName]->getPrimaryKey()->getColumns(); + } catch(SchemaException $e) { + $primaryKeyColumns = array(); + } + + if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) { + $foreignKeys = $this->tables[$tableName]->getForeignKeys(); + } else { + $foreignKeys = array(); + } + + $allForeignKeyColumns = array(); + foreach ($foreignKeys AS $foreignKey) { + $allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns()); + } + + $ids = array(); + $fieldMappings = array(); + foreach ($columns as $column) { + $fieldMapping = array(); + + if (in_array($column->getName(), $allForeignKeyColumns)) { + continue; + } else if ($primaryKeyColumns && in_array($column->getName(), $primaryKeyColumns)) { + $fieldMapping['id'] = true; + } + + $fieldMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $column->getName(), false); + $fieldMapping['columnName'] = $column->getName(); + $fieldMapping['type'] = strtolower((string) $column->getType()); + + if ($column->getType() instanceof \Doctrine\DBAL\Types\StringType) { + $fieldMapping['length'] = $column->getLength(); + $fieldMapping['fixed'] = $column->getFixed(); + } else if ($column->getType() instanceof \Doctrine\DBAL\Types\IntegerType) { + $fieldMapping['unsigned'] = $column->getUnsigned(); + } + $fieldMapping['nullable'] = $column->getNotNull() ? false : true; + + if (isset($fieldMapping['id'])) { + $ids[] = $fieldMapping; + } else { + $fieldMappings[] = $fieldMapping; + } + } + + if ($ids) { + if (count($ids) == 1) { + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + } + + foreach ($ids as $id) { + $metadata->mapField($id); + } + } + + foreach ($fieldMappings as $fieldMapping) { + $metadata->mapField($fieldMapping); + } + + foreach ($this->manyToManyTables AS $manyTable) { + foreach ($manyTable->getForeignKeys() AS $foreignKey) { + // foreign key maps to the table of the current entity, many to many association probably exists + if (strtolower($tableName) == strtolower($foreignKey->getForeignTableName())) { + $myFk = $foreignKey; + $otherFk = null; + foreach ($manyTable->getForeignKeys() AS $foreignKey) { + if ($foreignKey != $myFk) { + $otherFk = $foreignKey; + break; + } + } + + if (!$otherFk) { + // the definition of this many to many table does not contain + // enough foreign key information to continue reverse engeneering. + continue; + } + + $localColumn = current($myFk->getColumns()); + $associationMapping = array(); + $associationMapping['fieldName'] = $this->getFieldNameForColumn($manyTable->getName(), current($otherFk->getColumns()), true); + $associationMapping['targetEntity'] = $this->getClassNameForTable($otherFk->getForeignTableName()); + if (current($manyTable->getColumns())->getName() == $localColumn) { + $associationMapping['inversedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true); + $associationMapping['joinTable'] = array( + 'name' => strtolower($manyTable->getName()), + 'joinColumns' => array(), + 'inverseJoinColumns' => array(), + ); + + $fkCols = $myFk->getForeignColumns(); + $cols = $myFk->getColumns(); + for ($i = 0; $i < count($cols); $i++) { + $associationMapping['joinTable']['joinColumns'][] = array( + 'name' => $cols[$i], + 'referencedColumnName' => $fkCols[$i], + ); + } + + $fkCols = $otherFk->getForeignColumns(); + $cols = $otherFk->getColumns(); + for ($i = 0; $i < count($cols); $i++) { + $associationMapping['joinTable']['inverseJoinColumns'][] = array( + 'name' => $cols[$i], + 'referencedColumnName' => $fkCols[$i], + ); + } + } else { + $associationMapping['mappedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true); + } + $metadata->mapManyToMany($associationMapping); + break; + } + } + } + + foreach ($foreignKeys as $foreignKey) { + $foreignTable = $foreignKey->getForeignTableName(); + $cols = $foreignKey->getColumns(); + $fkCols = $foreignKey->getForeignColumns(); + + $localColumn = current($cols); + $associationMapping = array(); + $associationMapping['fieldName'] = $this->getFieldNameForColumn($tableName, $localColumn, true); + $associationMapping['targetEntity'] = $this->getClassNameForTable($foreignTable); + + if ($primaryKeyColumns && in_array($localColumn, $primaryKeyColumns)) { + $associationMapping['id'] = true; + } + + for ($i = 0; $i < count($cols); $i++) { + $associationMapping['joinColumns'][] = array( + 'name' => $cols[$i], + 'referencedColumnName' => $fkCols[$i], + ); + } + + //Here we need to check if $cols are the same as $primaryKeyColums + if (!array_diff($cols,$primaryKeyColumns)) { + $metadata->mapOneToOne($associationMapping); + } else { + $metadata->mapManyToOne($associationMapping); + } + } + } + + /** + * {@inheritdoc} + */ + public function isTransient($className) + { + return true; + } + + /** + * Return all the class names supported by this driver. + * + * IMPORTANT: This method must return an array of class not tables names. + * + * @return array + */ + public function getAllClassNames() + { + $this->reverseEngineerMappingFromDatabase(); + + return array_keys($this->classToTableNames); + } + + /** + * Set class name for a table. + * + * @param string $tableName + * @param string $className + * @return void + */ + public function setClassNameForTable($tableName, $className) + { + $this->classNamesForTables[$tableName] = $className; + } + + /** + * Set field name for a column on a specific table. + * + * @param string $tableName + * @param string $columnName + * @param string $fieldName + * @return void + */ + public function setFieldNameForColumn($tableName, $columnName, $fieldName) + { + $this->fieldNamesForColumns[$tableName][$columnName] = $fieldName; + } + + /** + * Return the mapped class name for a table if it exists. Otherwise return "classified" version. + * + * @param string $tableName + * @return string + */ + private function getClassNameForTable($tableName) + { + if (isset($this->classNamesForTables[$tableName])) { + return $this->namespace . $this->classNamesForTables[$tableName]; + } + + return $this->namespace . Inflector::classify(strtolower($tableName)); + } + + /** + * Return the mapped field name for a column, if it exists. Otherwise return camelized version. + * + * @param string $tableName + * @param string $columnName + * @param boolean $fk Whether the column is a foreignkey or not. + * @return string + */ + private function getFieldNameForColumn($tableName, $columnName, $fk = false) + { + if (isset($this->fieldNamesForColumns[$tableName]) && isset($this->fieldNamesForColumns[$tableName][$columnName])) { + return $this->fieldNamesForColumns[$tableName][$columnName]; + } + + $columnName = strtolower($columnName); + + // Replace _id if it is a foreignkey column + if ($fk) { + $columnName = str_replace('_id', '', $columnName); + } + return Inflector::camelize($columnName); + } + + /** + * Set the namespace for the generated entities. + * + * @param string $namespace + * @return void + */ + public function setNamespace($namespace) + { + $this->namespace = $namespace; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php new file mode 100644 index 0000000..290fc65 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -0,0 +1,54 @@ +. + */ + +require_once __DIR__.'/../Annotation.php'; +require_once __DIR__.'/../Entity.php'; +require_once __DIR__.'/../MappedSuperclass.php'; +require_once __DIR__.'/../InheritanceType.php'; +require_once __DIR__.'/../DiscriminatorColumn.php'; +require_once __DIR__.'/../DiscriminatorMap.php'; +require_once __DIR__.'/../Id.php'; +require_once __DIR__.'/../GeneratedValue.php'; +require_once __DIR__.'/../Version.php'; +require_once __DIR__.'/../JoinColumn.php'; +require_once __DIR__.'/../JoinColumns.php'; +require_once __DIR__.'/../Column.php'; +require_once __DIR__.'/../OneToOne.php'; +require_once __DIR__.'/../OneToMany.php'; +require_once __DIR__.'/../ManyToOne.php'; +require_once __DIR__.'/../ManyToMany.php'; +require_once __DIR__.'/../ElementCollection.php'; +require_once __DIR__.'/../Table.php'; +require_once __DIR__.'/../UniqueConstraint.php'; +require_once __DIR__.'/../Index.php'; +require_once __DIR__.'/../JoinTable.php'; +require_once __DIR__.'/../SequenceGenerator.php'; +require_once __DIR__.'/../ChangeTrackingPolicy.php'; +require_once __DIR__.'/../OrderBy.php'; +require_once __DIR__.'/../NamedQueries.php'; +require_once __DIR__.'/../NamedQuery.php'; +require_once __DIR__.'/../HasLifecycleCallbacks.php'; +require_once __DIR__.'/../PrePersist.php'; +require_once __DIR__.'/../PostPersist.php'; +require_once __DIR__.'/../PreUpdate.php'; +require_once __DIR__.'/../PostUpdate.php'; +require_once __DIR__.'/../PreRemove.php'; +require_once __DIR__.'/../PostRemove.php'; +require_once __DIR__.'/../PostLoad.php'; +require_once __DIR__.'/../PreFlush.php'; diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/Driver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/Driver.php new file mode 100644 index 0000000..28654a8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/Driver.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * Contract for metadata drivers. + * + * @since 2.0 + * @author Jonathan H. Wage + * @todo Rename: MetadataDriver or MappingDriver + */ +interface Driver +{ + /** + * Loads the metadata for the specified class into the provided container. + * + * @param string $className + * @param ClassMetadataInfo $metadata + */ + function loadMetadataForClass($className, ClassMetadataInfo $metadata); + + /** + * Gets the names of all mapped classes known to this driver. + * + * @return array The names of all mapped classes known to this driver. + */ + function getAllClassNames(); + + /** + * Whether the class with the specified name should have its metadata loaded. + * This is only the case if it is either mapped as an Entity or a + * MappedSuperclass. + * + * @param string $className + * @return boolean + */ + function isTransient($className); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php new file mode 100644 index 0000000..321962d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php @@ -0,0 +1,126 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\ORM\Mapping\Driver\Driver, + Doctrine\ORM\Mapping\ClassMetadataInfo, + Doctrine\ORM\Mapping\MappingException; + +/** + * The DriverChain allows you to add multiple other mapping drivers for + * certain namespaces + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + * @todo Rename: MappingDriverChain or MetadataDriverChain + */ +class DriverChain implements Driver +{ + /** + * @var array + */ + private $_drivers = array(); + + /** + * Add a nested driver. + * + * @param Driver $nestedDriver + * @param string $namespace + */ + public function addDriver(Driver $nestedDriver, $namespace) + { + $this->_drivers[$namespace] = $nestedDriver; + } + + /** + * Get the array of nested drivers. + * + * @return array $drivers + */ + public function getDrivers() + { + return $this->_drivers; + } + + /** + * Loads the metadata for the specified class into the provided container. + * + * @param string $className + * @param ClassMetadataInfo $metadata + */ + public function loadMetadataForClass($className, ClassMetadataInfo $metadata) + { + foreach ($this->_drivers as $namespace => $driver) { + if (strpos($className, $namespace) === 0) { + $driver->loadMetadataForClass($className, $metadata); + return; + } + } + + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); + } + + /** + * Gets the names of all mapped classes known to this driver. + * + * @return array The names of all mapped classes known to this driver. + */ + public function getAllClassNames() + { + $classNames = array(); + $driverClasses = array(); + foreach ($this->_drivers AS $namespace => $driver) { + $oid = spl_object_hash($driver); + if (!isset($driverClasses[$oid])) { + $driverClasses[$oid] = $driver->getAllClassNames(); + } + + foreach ($driverClasses[$oid] AS $className) { + if (strpos($className, $namespace) === 0) { + $classNames[$className] = true; + } + } + } + return array_keys($classNames); + } + + /** + * Whether the class with the specified name should have its metadata loaded. + * + * This is only the case for non-transient classes either mapped as an Entity or MappedSuperclass. + * + * @param string $className + * @return boolean + */ + public function isTransient($className) + { + foreach ($this->_drivers AS $namespace => $driver) { + if (strpos($className, $namespace) === 0) { + return $driver->isTransient($className); + } + } + + // class isTransient, i.e. not an entity or mapped superclass + return true; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/PHPDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/PHPDriver.php new file mode 100644 index 0000000..ff86597 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/PHPDriver.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Cache\ArrayCache, + Doctrine\Common\Annotations\AnnotationReader, + Doctrine\DBAL\Schema\AbstractSchemaManager, + Doctrine\ORM\Mapping\ClassMetadataInfo, + Doctrine\ORM\Mapping\MappingException, + Doctrine\Common\Util\Inflector, + Doctrine\ORM\Mapping\Driver\AbstractFileDriver; + +/** + * The PHPDriver includes php files which just populate ClassMetadataInfo + * instances with plain php code + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + * @todo Rename: PHPDriver + */ +class PHPDriver extends AbstractFileDriver +{ + /** + * {@inheritdoc} + */ + protected $_fileExtension = '.php'; + protected $_metadata; + + /** + * {@inheritdoc} + */ + public function loadMetadataForClass($className, ClassMetadataInfo $metadata) + { + $this->_metadata = $metadata; + $this->_loadMappingFile($this->_findMappingFile($className)); + } + + /** + * {@inheritdoc} + */ + protected function _loadMappingFile($file) + { + $metadata = $this->_metadata; + include $file; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedXmlDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedXmlDriver.php new file mode 100644 index 0000000..e60eab7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedXmlDriver.php @@ -0,0 +1,176 @@ +. +*/ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\ORM\Mapping\MappingException; + +/** + * XmlDriver that additionally looks for mapping information in a global file. + * + * @author Fabien Potencier + * @author Benjamin Eberlei + * @license MIT + */ +class SimplifiedXmlDriver extends XmlDriver +{ + protected $_prefixes = array(); + protected $_globalBasename; + protected $_classCache; + protected $_fileExtension = '.orm.xml'; + + public function __construct($prefixes) + { + $this->addNamespacePrefixes($prefixes); + } + + public function setGlobalBasename($file) + { + $this->_globalBasename = $file; + } + + public function getGlobalBasename() + { + return $this->_globalBasename; + } + + public function addNamespacePrefixes($prefixes) + { + $this->_prefixes = array_merge($this->_prefixes, $prefixes); + $this->addPaths(array_flip($prefixes)); + } + + public function getNamespacePrefixes() + { + return $this->_prefixes; + } + + public function isTransient($className) + { + if (null === $this->_classCache) { + $this->initialize(); + } + + // The mapping is defined in the global mapping file + if (isset($this->_classCache[$className])) { + return false; + } + + try { + $this->_findMappingFile($className); + + return false; + } catch (MappingException $e) { + return true; + } + } + + public function getAllClassNames() + { + if (null === $this->_classCache) { + $this->initialize(); + } + + $classes = array(); + + if ($this->_paths) { + foreach ((array) $this->_paths as $path) { + if (!is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + $fileName = $file->getBasename($this->_fileExtension); + + if ($fileName == $file->getBasename() || $fileName == $this->_globalBasename) { + continue; + } + + // NOTE: All files found here means classes are not transient! + if (isset($this->_prefixes[$path])) { + $classes[] = $this->_prefixes[$path].'\\'.str_replace('.', '\\', $fileName); + } else { + $classes[] = str_replace('.', '\\', $fileName); + } + } + } + } + + return array_merge($classes, array_keys($this->_classCache)); + } + + public function getElement($className) + { + if (null === $this->_classCache) { + $this->initialize(); + } + + if (!isset($this->_classCache[$className])) { + $this->_classCache[$className] = parent::getElement($className); + } + + return $this->_classCache[$className]; + } + + protected function initialize() + { + $this->_classCache = array(); + if (null !== $this->_globalBasename) { + foreach ($this->_paths as $path) { + if (is_file($file = $path.'/'.$this->_globalBasename.$this->_fileExtension)) { + $this->_classCache = array_merge($this->_classCache, $this->_loadMappingFile($file)); + } + } + } + } + + protected function _findMappingFile($className) + { + $defaultFileName = str_replace('\\', '.', $className).$this->_fileExtension; + foreach ($this->_paths as $path) { + if (!isset($this->_prefixes[$path])) { + if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) { + return $path.DIRECTORY_SEPARATOR.$defaultFileName; + } + + continue; + } + + $prefix = $this->_prefixes[$path]; + + if (0 !== strpos($className, $prefix.'\\')) { + continue; + } + + $filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', '.').$this->_fileExtension; + if (is_file($filename)) { + return $filename; + } + + throw MappingException::mappingFileNotFound($className, $filename); + } + + throw MappingException::mappingFileNotFound($className, substr($className, strrpos($className, '\\') + 1).$this->_fileExtension); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedYamlDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedYamlDriver.php new file mode 100644 index 0000000..b88a769 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedYamlDriver.php @@ -0,0 +1,181 @@ +. +*/ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\ORM\Mapping\MappingException; + +/** + * YamlDriver that additionally looks for mapping information in a global file. + * + * @author Fabien Potencier + * @author Benjamin Eberlei + * @license MIT + */ +class SimplifiedYamlDriver extends YamlDriver +{ + protected $_prefixes = array(); + protected $_globalBasename; + protected $_classCache; + protected $_fileExtension = '.orm.yml'; + + public function __construct($prefixes) + { + $this->addNamespacePrefixes($prefixes); + } + + public function setGlobalBasename($file) + { + $this->_globalBasename = $file; + } + + public function getGlobalBasename() + { + return $this->_globalBasename; + } + + public function addNamespacePrefixes($prefixes) + { + $this->_prefixes = array_merge($this->_prefixes, $prefixes); + $this->addPaths(array_flip($prefixes)); + } + + public function addNamespacePrefix($prefix, $path) + { + $this->_prefixes[$path] = $prefix; + } + + public function getNamespacePrefixes() + { + return $this->_prefixes; + } + + public function isTransient($className) + { + if (null === $this->_classCache) { + $this->initialize(); + } + + // The mapping is defined in the global mapping file + if (isset($this->_classCache[$className])) { + return false; + } + + try { + $this->_findMappingFile($className); + + return false; + } catch (MappingException $e) { + return true; + } + } + + public function getAllClassNames() + { + if (null === $this->_classCache) { + $this->initialize(); + } + + $classes = array(); + + if ($this->_paths) { + foreach ((array) $this->_paths as $path) { + if (!is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + $fileName = $file->getBasename($this->_fileExtension); + + if ($fileName == $file->getBasename() || $fileName == $this->_globalBasename) { + continue; + } + + // NOTE: All files found here means classes are not transient! + if (isset($this->_prefixes[$path])) { + $classes[] = $this->_prefixes[$path].'\\'.str_replace('.', '\\', $fileName); + } else { + $classes[] = str_replace('.', '\\', $fileName); + } + } + } + } + + return array_merge($classes, array_keys($this->_classCache)); + } + + public function getElement($className) + { + if (null === $this->_classCache) { + $this->initialize(); + } + + if (!isset($this->_classCache[$className])) { + $this->_classCache[$className] = parent::getElement($className); + } + + return $this->_classCache[$className]; + } + + protected function initialize() + { + $this->_classCache = array(); + if (null !== $this->_globalBasename) { + foreach ($this->_paths as $path) { + if (is_file($file = $path.'/'.$this->_globalBasename.$this->_fileExtension)) { + $this->_classCache = array_merge($this->_classCache, $this->_loadMappingFile($file)); + } + } + } + } + + protected function _findMappingFile($className) + { + $defaultFileName = str_replace('\\', '.', $className).$this->_fileExtension; + foreach ($this->_paths as $path) { + if (!isset($this->_prefixes[$path])) { + if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) { + return $path.DIRECTORY_SEPARATOR.$defaultFileName; + } + + continue; + } + + $prefix = $this->_prefixes[$path]; + + if (0 !== strpos($className, $prefix.'\\')) { + continue; + } + + $filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', '.').$this->_fileExtension; + if (is_file($filename)) { + return $filename; + } + + throw MappingException::mappingFileNotFound($className, $filename); + } + + throw MappingException::mappingFileNotFound($className, substr($className, strrpos($className, '\\') + 1).$this->_fileExtension); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php new file mode 100644 index 0000000..4e593ea --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php @@ -0,0 +1,138 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo, + Doctrine\ORM\Mapping\MappingException; + +/** + * The StaticPHPDriver calls a static loadMetadata() method on your entity + * classes where you can manually populate the ClassMetadata instance. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class StaticPHPDriver implements Driver +{ + /** + * Paths of entity directories. + * + * @var array + */ + private $_paths = array(); + + /** + * Map of all class names. + * + * @var array + */ + private $_classNames; + + /** + * The file extension of mapping documents. + * + * @var string + */ + private $_fileExtension = '.php'; + + public function __construct($paths) + { + $this->addPaths((array) $paths); + } + + public function addPaths(array $paths) + { + $this->_paths = array_unique(array_merge($this->_paths, $paths)); + } + + /** + * {@inheritdoc} + */ + public function loadMetadataForClass($className, ClassMetadataInfo $metadata) + { + call_user_func_array(array($className, 'loadMetadata'), array($metadata)); + } + + /** + * {@inheritDoc} + * @todo Same code exists in AnnotationDriver, should we re-use it somehow or not worry about it? + */ + public function getAllClassNames() + { + if ($this->_classNames !== null) { + return $this->_classNames; + } + + if (!$this->_paths) { + throw MappingException::pathRequired(); + } + + $classes = array(); + $includedFiles = array(); + + foreach ($this->_paths as $path) { + if (!is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + if (($fileName = $file->getBasename($this->_fileExtension)) == $file->getBasename()) { + continue; + } + + $sourceFile = realpath($file->getPathName()); + require_once $sourceFile; + $includedFiles[] = $sourceFile; + } + } + + $declared = get_declared_classes(); + + foreach ($declared as $className) { + $rc = new \ReflectionClass($className); + $sourceFile = $rc->getFileName(); + if (in_array($sourceFile, $includedFiles) && !$this->isTransient($className)) { + $classes[] = $className; + } + } + + $this->_classNames = $classes; + + return $classes; + } + + /** + * {@inheritdoc} + */ + public function isTransient($className) + { + return method_exists($className, 'loadMetadata') ? false : true; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php new file mode 100644 index 0000000..31f3191 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -0,0 +1,536 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use SimpleXMLElement, + Doctrine\ORM\Mapping\ClassMetadataInfo, + Doctrine\ORM\Mapping\MappingException; + +/** + * XmlDriver is a metadata driver that enables mapping through XML files. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class XmlDriver extends AbstractFileDriver +{ + /** + * {@inheritdoc} + */ + protected $_fileExtension = '.dcm.xml'; + + /** + * {@inheritdoc} + */ + public function loadMetadataForClass($className, ClassMetadataInfo $metadata) + { + $xmlRoot = $this->getElement($className); + + if ($xmlRoot->getName() == 'entity') { + if (isset($xmlRoot['repository-class'])) { + $metadata->setCustomRepositoryClass((string)$xmlRoot['repository-class']); + } + if (isset($xmlRoot['read-only']) && $xmlRoot['read-only'] == "true") { + $metadata->markReadOnly(); + } + } else if ($xmlRoot->getName() == 'mapped-superclass') { + $metadata->setCustomRepositoryClass( + isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null + ); + $metadata->isMappedSuperclass = true; + } else { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); + } + + // Evaluate attributes + $table = array(); + if (isset($xmlRoot['table'])) { + $table['name'] = (string)$xmlRoot['table']; + } + + $metadata->setPrimaryTable($table); + + // Evaluate named queries + if (isset($xmlRoot['named-queries'])) { + foreach ($xmlRoot->{'named-queries'}->{'named-query'} as $namedQueryElement) { + $metadata->addNamedQuery(array( + 'name' => (string)$namedQueryElement['name'], + 'query' => (string)$namedQueryElement['query'] + )); + } + } + + /* not implemented specially anyway. use table = schema.table + if (isset($xmlRoot['schema'])) { + $metadata->table['schema'] = (string)$xmlRoot['schema']; + }*/ + + if (isset($xmlRoot['inheritance-type'])) { + $inheritanceType = (string)$xmlRoot['inheritance-type']; + $metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType)); + + if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) { + // Evaluate + if (isset($xmlRoot->{'discriminator-column'})) { + $discrColumn = $xmlRoot->{'discriminator-column'}; + $metadata->setDiscriminatorColumn(array( + 'name' => (string)$discrColumn['name'], + 'type' => (string)$discrColumn['type'], + 'length' => (string)$discrColumn['length'] + )); + } else { + $metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255)); + } + + // Evaluate + if (isset($xmlRoot->{'discriminator-map'})) { + $map = array(); + foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} AS $discrMapElement) { + $map[(string)$discrMapElement['value']] = (string)$discrMapElement['class']; + } + $metadata->setDiscriminatorMap($map); + } + } + } + + + // Evaluate + if (isset($xmlRoot['change-tracking-policy'])) { + $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' + . strtoupper((string)$xmlRoot['change-tracking-policy']))); + } + + // Evaluate + if (isset($xmlRoot->indexes)) { + $metadata->table['indexes'] = array(); + foreach ($xmlRoot->indexes->index as $index) { + $columns = explode(',', (string)$index['columns']); + + if (isset($index['name'])) { + $metadata->table['indexes'][(string)$index['name']] = array( + 'columns' => $columns + ); + } else { + $metadata->table['indexes'][] = array( + 'columns' => $columns + ); + } + } + } + + // Evaluate + if (isset($xmlRoot->{'unique-constraints'})) { + $metadata->table['uniqueConstraints'] = array(); + foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $unique) { + $columns = explode(',', (string)$unique['columns']); + + if (isset($unique['name'])) { + $metadata->table['uniqueConstraints'][(string)$unique['name']] = array( + 'columns' => $columns + ); + } else { + $metadata->table['uniqueConstraints'][] = array( + 'columns' => $columns + ); + } + } + } + + // Evaluate mappings + if (isset($xmlRoot->field)) { + foreach ($xmlRoot->field as $fieldMapping) { + $mapping = array( + 'fieldName' => (string)$fieldMapping['name'], + ); + + if (isset($fieldMapping['type'])) { + $mapping['type'] = (string)$fieldMapping['type']; + } + + if (isset($fieldMapping['column'])) { + $mapping['columnName'] = (string)$fieldMapping['column']; + } + + if (isset($fieldMapping['length'])) { + $mapping['length'] = (int)$fieldMapping['length']; + } + + if (isset($fieldMapping['precision'])) { + $mapping['precision'] = (int)$fieldMapping['precision']; + } + + if (isset($fieldMapping['scale'])) { + $mapping['scale'] = (int)$fieldMapping['scale']; + } + + if (isset($fieldMapping['unique'])) { + $mapping['unique'] = ((string)$fieldMapping['unique'] == "false") ? false : true; + } + + if (isset($fieldMapping['options'])) { + $mapping['options'] = (array)$fieldMapping['options']; + } + + if (isset($fieldMapping['nullable'])) { + $mapping['nullable'] = ((string)$fieldMapping['nullable'] == "false") ? false : true; + } + + if (isset($fieldMapping['version']) && $fieldMapping['version']) { + $metadata->setVersionMapping($mapping); + } + + if (isset($fieldMapping['column-definition'])) { + $mapping['columnDefinition'] = (string)$fieldMapping['column-definition']; + } + + $metadata->mapField($mapping); + } + } + + // Evaluate mappings + $associationIds = array(); + foreach ($xmlRoot->id as $idElement) { + if ((bool)$idElement['association-key'] == true) { + $associationIds[(string)$idElement['name']] = true; + continue; + } + + $mapping = array( + 'id' => true, + 'fieldName' => (string)$idElement['name'] + ); + + if (isset($idElement['type'])) { + $mapping['type'] = (string)$idElement['type']; + } + + if (isset($idElement['column'])) { + $mapping['columnName'] = (string)$idElement['column']; + } + + if (isset($idElement['column-definition'])) { + $mapping['columnDefinition'] = (string)$idElement['column-definition']; + } + + $metadata->mapField($mapping); + + if (isset($idElement->generator)) { + $strategy = isset($idElement->generator['strategy']) ? + (string)$idElement->generator['strategy'] : 'AUTO'; + $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' + . $strategy)); + } + + // Check for SequenceGenerator/TableGenerator definition + if (isset($idElement->{'sequence-generator'})) { + $seqGenerator = $idElement->{'sequence-generator'}; + $metadata->setSequenceGeneratorDefinition(array( + 'sequenceName' => (string)$seqGenerator['sequence-name'], + 'allocationSize' => (string)$seqGenerator['allocation-size'], + 'initialValue' => (string)$seqGenerator['initial-value'] + )); + } else if (isset($idElement->{'table-generator'})) { + throw MappingException::tableIdGeneratorNotImplemented($className); + } + } + + // Evaluate mappings + if (isset($xmlRoot->{'one-to-one'})) { + foreach ($xmlRoot->{'one-to-one'} as $oneToOneElement) { + $mapping = array( + 'fieldName' => (string)$oneToOneElement['field'], + 'targetEntity' => (string)$oneToOneElement['target-entity'] + ); + + if (isset($associationIds[$mapping['fieldName']])) { + $mapping['id'] = true; + } + + if (isset($oneToOneElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$oneToOneElement['fetch']); + } + + if (isset($oneToOneElement['mapped-by'])) { + $mapping['mappedBy'] = (string)$oneToOneElement['mapped-by']; + } else { + if (isset($oneToOneElement['inversed-by'])) { + $mapping['inversedBy'] = (string)$oneToOneElement['inversed-by']; + } + $joinColumns = array(); + + if (isset($oneToOneElement->{'join-column'})) { + $joinColumns[] = $this->_getJoinColumnMapping($oneToOneElement->{'join-column'}); + } else if (isset($oneToOneElement->{'join-columns'})) { + foreach ($oneToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + } + + if (isset($oneToOneElement->cascade)) { + $mapping['cascade'] = $this->_getCascadeMappings($oneToOneElement->cascade); + } + + if (isset($oneToOneElement['orphan-removal'])) { + $mapping['orphanRemoval'] = (bool)$oneToOneElement['orphan-removal']; + } + + $metadata->mapOneToOne($mapping); + } + } + + // Evaluate mappings + if (isset($xmlRoot->{'one-to-many'})) { + foreach ($xmlRoot->{'one-to-many'} as $oneToManyElement) { + $mapping = array( + 'fieldName' => (string)$oneToManyElement['field'], + 'targetEntity' => (string)$oneToManyElement['target-entity'], + 'mappedBy' => (string)$oneToManyElement['mapped-by'] + ); + + if (isset($oneToManyElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$oneToManyElement['fetch']); + } + + if (isset($oneToManyElement->cascade)) { + $mapping['cascade'] = $this->_getCascadeMappings($oneToManyElement->cascade); + } + + if (isset($oneToManyElement['orphan-removal'])) { + $mapping['orphanRemoval'] = (bool)$oneToManyElement['orphan-removal']; + } + + if (isset($oneToManyElement->{'order-by'})) { + $orderBy = array(); + foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} AS $orderByField) { + $orderBy[(string)$orderByField['name']] = (string)$orderByField['direction']; + } + $mapping['orderBy'] = $orderBy; + } + + if (isset($oneToManyElement['index-by'])) { + $mapping['indexBy'] = (string)$oneToManyElement['index-by']; + } else if (isset($oneToManyElement->{'index-by'})) { + throw new \InvalidArgumentException(" is not a valid tag"); + } + + $metadata->mapOneToMany($mapping); + } + } + + // Evaluate mappings + if (isset($xmlRoot->{'many-to-one'})) { + foreach ($xmlRoot->{'many-to-one'} as $manyToOneElement) { + $mapping = array( + 'fieldName' => (string)$manyToOneElement['field'], + 'targetEntity' => (string)$manyToOneElement['target-entity'] + ); + + if (isset($associationIds[$mapping['fieldName']])) { + $mapping['id'] = true; + } + + if (isset($manyToOneElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToOneElement['fetch']); + } + + if (isset($manyToOneElement['inversed-by'])) { + $mapping['inversedBy'] = (string)$manyToOneElement['inversed-by']; + } + + $joinColumns = array(); + + if (isset($manyToOneElement->{'join-column'})) { + $joinColumns[] = $this->_getJoinColumnMapping($manyToOneElement->{'join-column'}); + } else if (isset($manyToOneElement->{'join-columns'})) { + foreach ($manyToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + + if (isset($manyToOneElement->cascade)) { + $mapping['cascade'] = $this->_getCascadeMappings($manyToOneElement->cascade); + } + + $metadata->mapManyToOne($mapping); + } + } + + // Evaluate mappings + if (isset($xmlRoot->{'many-to-many'})) { + foreach ($xmlRoot->{'many-to-many'} as $manyToManyElement) { + $mapping = array( + 'fieldName' => (string)$manyToManyElement['field'], + 'targetEntity' => (string)$manyToManyElement['target-entity'] + ); + + if (isset($manyToManyElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToManyElement['fetch']); + } + + if (isset($manyToManyElement['orphan-removal'])) { + $mapping['orphanRemoval'] = (bool)$manyToManyElement['orphan-removal']; + } + + if (isset($manyToManyElement['mapped-by'])) { + $mapping['mappedBy'] = (string)$manyToManyElement['mapped-by']; + } else if (isset($manyToManyElement->{'join-table'})) { + if (isset($manyToManyElement['inversed-by'])) { + $mapping['inversedBy'] = (string)$manyToManyElement['inversed-by']; + } + + $joinTableElement = $manyToManyElement->{'join-table'}; + $joinTable = array( + 'name' => (string)$joinTableElement['name'] + ); + + if (isset($joinTableElement['schema'])) { + $joinTable['schema'] = (string)$joinTableElement['schema']; + } + + foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['joinColumns'][] = $this->_getJoinColumnMapping($joinColumnElement); + } + + foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['inverseJoinColumns'][] = $this->_getJoinColumnMapping($joinColumnElement); + } + + $mapping['joinTable'] = $joinTable; + } + + if (isset($manyToManyElement->cascade)) { + $mapping['cascade'] = $this->_getCascadeMappings($manyToManyElement->cascade); + } + + if (isset($manyToManyElement->{'order-by'})) { + $orderBy = array(); + foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} AS $orderByField) { + $orderBy[(string)$orderByField['name']] = (string)$orderByField['direction']; + } + $mapping['orderBy'] = $orderBy; + } + + if (isset($manyToManyElement['index-by'])) { + $mapping['indexBy'] = (string)$manyToManyElement['index-by']; + } else if (isset($manyToManyElement->{'index-by'})) { + throw new \InvalidArgumentException(" is not a valid tag"); + } + + $metadata->mapManyToMany($mapping); + } + } + + // Evaluate + if (isset($xmlRoot->{'lifecycle-callbacks'})) { + foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) { + $metadata->addLifecycleCallback((string)$lifecycleCallback['method'], constant('Doctrine\ORM\Events::' . (string)$lifecycleCallback['type'])); + } + } + } + + /** + * Constructs a joinColumn mapping array based on the information + * found in the given SimpleXMLElement. + * + * @param $joinColumnElement The XML element. + * @return array The mapping array. + */ + private function _getJoinColumnMapping(SimpleXMLElement $joinColumnElement) + { + $joinColumn = array( + 'name' => (string)$joinColumnElement['name'], + 'referencedColumnName' => (string)$joinColumnElement['referenced-column-name'] + ); + + if (isset($joinColumnElement['unique'])) { + $joinColumn['unique'] = ((string)$joinColumnElement['unique'] == "false") ? false : true; + } + + if (isset($joinColumnElement['nullable'])) { + $joinColumn['nullable'] = ((string)$joinColumnElement['nullable'] == "false") ? false : true; + } + + if (isset($joinColumnElement['on-delete'])) { + $joinColumn['onDelete'] = (string)$joinColumnElement['on-delete']; + } + + if (isset($joinColumnElement['column-definition'])) { + $joinColumn['columnDefinition'] = (string)$joinColumnElement['column-definition']; + } + + return $joinColumn; + } + + /** + * Gathers a list of cascade options found in the given cascade element. + * + * @param $cascadeElement The cascade element. + * @return array The list of cascade options. + */ + private function _getCascadeMappings($cascadeElement) + { + $cascades = array(); + foreach ($cascadeElement->children() as $action) { + // According to the JPA specifications, XML uses "cascade-persist" + // instead of "persist". Here, both variations + // are supported because both YAML and Annotation use "persist" + // and we want to make sure that this driver doesn't need to know + // anything about the supported cascading actions + $cascades[] = str_replace('cascade-', '', $action->getName()); + } + return $cascades; + } + + /** + * {@inheritdoc} + */ + protected function _loadMappingFile($file) + { + $result = array(); + $xmlElement = simplexml_load_file($file); + + if (isset($xmlElement->entity)) { + foreach ($xmlElement->entity as $entityElement) { + $entityName = (string)$entityElement['name']; + $result[$entityName] = $entityElement; + } + } else if (isset($xmlElement->{'mapped-superclass'})) { + foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) { + $className = (string)$mappedSuperClass['name']; + $result[$className] = $mappedSuperClass; + } + } + + return $result; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php new file mode 100644 index 0000000..c8baa2f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -0,0 +1,512 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo, + Doctrine\ORM\Mapping\MappingException; + +/** + * The YamlDriver reads the mapping metadata from yaml schema files. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class YamlDriver extends AbstractFileDriver +{ + /** + * {@inheritdoc} + */ + protected $_fileExtension = '.dcm.yml'; + + /** + * {@inheritdoc} + */ + public function loadMetadataForClass($className, ClassMetadataInfo $metadata) + { + $element = $this->getElement($className); + + if ($element['type'] == 'entity') { + if (isset($element['repositoryClass'])) { + $metadata->setCustomRepositoryClass($element['repositoryClass']); + } + if (isset($element['readOnly']) && $element['readOnly'] == true) { + $metadata->markReadOnly(); + } + } else if ($element['type'] == 'mappedSuperclass') { + $metadata->setCustomRepositoryClass( + isset($element['repositoryClass']) ? $element['repositoryClass'] : null + ); + $metadata->isMappedSuperclass = true; + } else { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); + } + + // Evaluate root level properties + $table = array(); + if (isset($element['table'])) { + $table['name'] = $element['table']; + } + $metadata->setPrimaryTable($table); + + // Evaluate named queries + if (isset($element['namedQueries'])) { + foreach ($element['namedQueries'] as $name => $queryMapping) { + if (is_string($queryMapping)) { + $queryMapping = array('query' => $queryMapping); + } + + if ( ! isset($queryMapping['name'])) { + $queryMapping['name'] = $name; + } + + $metadata->addNamedQuery($queryMapping); + } + } + + /* not implemented specially anyway. use table = schema.table + if (isset($element['schema'])) { + $metadata->table['schema'] = $element['schema']; + }*/ + + if (isset($element['inheritanceType'])) { + $metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . strtoupper($element['inheritanceType']))); + + if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) { + // Evaluate discriminatorColumn + if (isset($element['discriminatorColumn'])) { + $discrColumn = $element['discriminatorColumn']; + $metadata->setDiscriminatorColumn(array( + 'name' => $discrColumn['name'], + 'type' => $discrColumn['type'], + 'length' => $discrColumn['length'] + )); + } else { + $metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255)); + } + + // Evaluate discriminatorMap + if (isset($element['discriminatorMap'])) { + $metadata->setDiscriminatorMap($element['discriminatorMap']); + } + } + } + + + // Evaluate changeTrackingPolicy + if (isset($element['changeTrackingPolicy'])) { + $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' + . strtoupper($element['changeTrackingPolicy']))); + } + + // Evaluate indexes + if (isset($element['indexes'])) { + foreach ($element['indexes'] as $name => $index) { + if ( ! isset($index['name'])) { + $index['name'] = $name; + } + + if (is_string($index['columns'])) { + $columns = explode(',', $index['columns']); + } else { + $columns = $index['columns']; + } + + $metadata->table['indexes'][$index['name']] = array( + 'columns' => $columns + ); + } + } + + // Evaluate uniqueConstraints + if (isset($element['uniqueConstraints'])) { + foreach ($element['uniqueConstraints'] as $name => $unique) { + if ( ! isset($unique['name'])) { + $unique['name'] = $name; + } + + if (is_string($unique['columns'])) { + $columns = explode(',', $unique['columns']); + } else { + $columns = $unique['columns']; + } + + $metadata->table['uniqueConstraints'][$unique['name']] = array( + 'columns' => $columns + ); + } + } + + $associationIds = array(); + if (isset($element['id'])) { + // Evaluate identifier settings + foreach ($element['id'] as $name => $idElement) { + if (isset($idElement['associationKey']) && $idElement['associationKey'] == true) { + $associationIds[$name] = true; + continue; + } + + $mapping = array( + 'id' => true, + 'fieldName' => $name + ); + + if (isset($idElement['type'])) { + $mapping['type'] = $idElement['type']; + } + + if (isset($idElement['column'])) { + $mapping['columnName'] = $idElement['column']; + } + + if (isset($idElement['length'])) { + $mapping['length'] = $idElement['length']; + } + + if (isset($idElement['columnDefinition'])) { + $mapping['columnDefinition'] = $idElement['columnDefinition']; + } + + $metadata->mapField($mapping); + + if (isset($idElement['generator'])) { + $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' + . strtoupper($idElement['generator']['strategy']))); + } + // Check for SequenceGenerator/TableGenerator definition + if (isset($idElement['sequenceGenerator'])) { + $metadata->setSequenceGeneratorDefinition($idElement['sequenceGenerator']); + } else if (isset($idElement['tableGenerator'])) { + throw MappingException::tableIdGeneratorNotImplemented($className); + } + } + } + + // Evaluate fields + if (isset($element['fields'])) { + foreach ($element['fields'] as $name => $fieldMapping) { + + $mapping = array( + 'fieldName' => $name + ); + + if (isset($fieldMapping['type'])) { + $e = explode('(', $fieldMapping['type']); + $fieldMapping['type'] = $e[0]; + $mapping['type'] = $fieldMapping['type']; + + if (isset($e[1])) { + $fieldMapping['length'] = substr($e[1], 0, strlen($e[1]) - 1); + } + } + + if (isset($fieldMapping['id'])) { + $mapping['id'] = true; + if (isset($fieldMapping['generator']['strategy'])) { + $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' + . strtoupper($fieldMapping['generator']['strategy']))); + } + } + if (isset($fieldMapping['column'])) { + $mapping['columnName'] = $fieldMapping['column']; + } + if (isset($fieldMapping['length'])) { + $mapping['length'] = $fieldMapping['length']; + } + if (isset($fieldMapping['precision'])) { + $mapping['precision'] = $fieldMapping['precision']; + } + if (isset($fieldMapping['scale'])) { + $mapping['scale'] = $fieldMapping['scale']; + } + if (isset($fieldMapping['unique'])) { + $mapping['unique'] = (bool)$fieldMapping['unique']; + } + if (isset($fieldMapping['options'])) { + $mapping['options'] = $fieldMapping['options']; + } + if (isset($fieldMapping['nullable'])) { + $mapping['nullable'] = $fieldMapping['nullable']; + } + if (isset($fieldMapping['version']) && $fieldMapping['version']) { + $metadata->setVersionMapping($mapping); + } + if (isset($fieldMapping['columnDefinition'])) { + $mapping['columnDefinition'] = $fieldMapping['columnDefinition']; + } + + $metadata->mapField($mapping); + } + } + + // Evaluate oneToOne relationships + if (isset($element['oneToOne'])) { + foreach ($element['oneToOne'] as $name => $oneToOneElement) { + $mapping = array( + 'fieldName' => $name, + 'targetEntity' => $oneToOneElement['targetEntity'] + ); + + if (isset($associationIds[$mapping['fieldName']])) { + $mapping['id'] = true; + } + + if (isset($oneToOneElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneElement['fetch']); + } + + if (isset($oneToOneElement['mappedBy'])) { + $mapping['mappedBy'] = $oneToOneElement['mappedBy']; + } else { + if (isset($oneToOneElement['inversedBy'])) { + $mapping['inversedBy'] = $oneToOneElement['inversedBy']; + } + + $joinColumns = array(); + + if (isset($oneToOneElement['joinColumn'])) { + $joinColumns[] = $this->_getJoinColumnMapping($oneToOneElement['joinColumn']); + } else if (isset($oneToOneElement['joinColumns'])) { + foreach ($oneToOneElement['joinColumns'] as $name => $joinColumnElement) { + if (!isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + + $joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + } + + if (isset($oneToOneElement['cascade'])) { + $mapping['cascade'] = $oneToOneElement['cascade']; + } + + if (isset($oneToOneElement['orphanRemoval'])) { + $mapping['orphanRemoval'] = (bool)$oneToOneElement['orphanRemoval']; + } + + $metadata->mapOneToOne($mapping); + } + } + + // Evaluate oneToMany relationships + if (isset($element['oneToMany'])) { + foreach ($element['oneToMany'] as $name => $oneToManyElement) { + $mapping = array( + 'fieldName' => $name, + 'targetEntity' => $oneToManyElement['targetEntity'], + 'mappedBy' => $oneToManyElement['mappedBy'] + ); + + if (isset($oneToManyElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyElement['fetch']); + } + + if (isset($oneToManyElement['cascade'])) { + $mapping['cascade'] = $oneToManyElement['cascade']; + } + + if (isset($oneToManyElement['orphanRemoval'])) { + $mapping['orphanRemoval'] = (bool)$oneToManyElement['orphanRemoval']; + } + + if (isset($oneToManyElement['orderBy'])) { + $mapping['orderBy'] = $oneToManyElement['orderBy']; + } + + if (isset($oneToManyElement['indexBy'])) { + $mapping['indexBy'] = $oneToManyElement['indexBy']; + } + + $metadata->mapOneToMany($mapping); + } + } + + // Evaluate manyToOne relationships + if (isset($element['manyToOne'])) { + foreach ($element['manyToOne'] as $name => $manyToOneElement) { + $mapping = array( + 'fieldName' => $name, + 'targetEntity' => $manyToOneElement['targetEntity'] + ); + + if (isset($associationIds[$mapping['fieldName']])) { + $mapping['id'] = true; + } + + if (isset($manyToOneElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneElement['fetch']); + } + + if (isset($manyToOneElement['inversedBy'])) { + $mapping['inversedBy'] = $manyToOneElement['inversedBy']; + } + + $joinColumns = array(); + + if (isset($manyToOneElement['joinColumn'])) { + $joinColumns[] = $this->_getJoinColumnMapping($manyToOneElement['joinColumn']); + } else if (isset($manyToOneElement['joinColumns'])) { + foreach ($manyToOneElement['joinColumns'] as $name => $joinColumnElement) { + if (!isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + + $joinColumns[] = $this->_getJoinColumnMapping($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + + if (isset($manyToOneElement['cascade'])) { + $mapping['cascade'] = $manyToOneElement['cascade']; + } + + $metadata->mapManyToOne($mapping); + } + } + + // Evaluate manyToMany relationships + if (isset($element['manyToMany'])) { + foreach ($element['manyToMany'] as $name => $manyToManyElement) { + $mapping = array( + 'fieldName' => $name, + 'targetEntity' => $manyToManyElement['targetEntity'] + ); + + if (isset($manyToManyElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyElement['fetch']); + } + + if (isset($manyToManyElement['mappedBy'])) { + $mapping['mappedBy'] = $manyToManyElement['mappedBy']; + } else if (isset($manyToManyElement['joinTable'])) { + + $joinTableElement = $manyToManyElement['joinTable']; + $joinTable = array( + 'name' => $joinTableElement['name'] + ); + + if (isset($joinTableElement['schema'])) { + $joinTable['schema'] = $joinTableElement['schema']; + } + + foreach ($joinTableElement['joinColumns'] as $name => $joinColumnElement) { + if (!isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + + $joinTable['joinColumns'][] = $this->_getJoinColumnMapping($joinColumnElement); + } + + foreach ($joinTableElement['inverseJoinColumns'] as $name => $joinColumnElement) { + if (!isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + + $joinTable['inverseJoinColumns'][] = $this->_getJoinColumnMapping($joinColumnElement); + } + + $mapping['joinTable'] = $joinTable; + } + + if (isset($manyToManyElement['inversedBy'])) { + $mapping['inversedBy'] = $manyToManyElement['inversedBy']; + } + + if (isset($manyToManyElement['cascade'])) { + $mapping['cascade'] = $manyToManyElement['cascade']; + } + + if (isset($manyToManyElement['orderBy'])) { + $mapping['orderBy'] = $manyToManyElement['orderBy']; + } + + if (isset($manyToManyElement['indexBy'])) { + $mapping['indexBy'] = $manyToManyElement['indexBy']; + } + + if (isset($manyToManyElement['orphanRemoval'])) { + $mapping['orphanRemoval'] = (bool)$manyToManyElement['orphanRemoval']; + } + + $metadata->mapManyToMany($mapping); + } + } + + // Evaluate lifeCycleCallbacks + if (isset($element['lifecycleCallbacks'])) { + foreach ($element['lifecycleCallbacks'] as $type => $methods) { + foreach ($methods as $method) { + $metadata->addLifecycleCallback($method, constant('Doctrine\ORM\Events::' . $type)); + } + } + } + } + + /** + * Constructs a joinColumn mapping array based on the information + * found in the given join column element. + * + * @param $joinColumnElement The array join column element + * @return array The mapping array. + */ + private function _getJoinColumnMapping($joinColumnElement) + { + $joinColumn = array( + 'name' => $joinColumnElement['name'], + 'referencedColumnName' => $joinColumnElement['referencedColumnName'] + ); + + if (isset($joinColumnElement['fieldName'])) { + $joinColumn['fieldName'] = (string) $joinColumnElement['fieldName']; + } + + if (isset($joinColumnElement['unique'])) { + $joinColumn['unique'] = (bool) $joinColumnElement['unique']; + } + + if (isset($joinColumnElement['nullable'])) { + $joinColumn['nullable'] = (bool) $joinColumnElement['nullable']; + } + + if (isset($joinColumnElement['onDelete'])) { + $joinColumn['onDelete'] = $joinColumnElement['onDelete']; + } + + if (isset($joinColumnElement['columnDefinition'])) { + $joinColumn['columnDefinition'] = $joinColumnElement['columnDefinition']; + } + + return $joinColumn; + } + + /** + * {@inheritdoc} + */ + protected function _loadMappingFile($file) + { + return \Symfony\Component\Yaml\Yaml::parse($file); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ElementCollection.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ElementCollection.php new file mode 100644 index 0000000..f127174 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ElementCollection.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ALL") + * @todo check available targets + */ +final class ElementCollection implements Annotation +{ + /** @var string */ + public $tableName; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Entity.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Entity.php new file mode 100644 index 0000000..075a5ec --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Entity.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class Entity implements Annotation +{ + /** @var string */ + public $repositoryClass; + /** @var boolean */ + public $readOnly = false; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/GeneratedValue.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/GeneratedValue.php new file mode 100644 index 0000000..8558d21 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/GeneratedValue.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class GeneratedValue implements Annotation +{ + /** @var string */ + public $strategy = 'AUTO'; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/HasLifecycleCallbacks.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/HasLifecycleCallbacks.php new file mode 100644 index 0000000..a65fbb5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/HasLifecycleCallbacks.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class HasLifecycleCallbacks implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Id.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Id.php new file mode 100644 index 0000000..a670e3d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Id.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class Id implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Index.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Index.php new file mode 100644 index 0000000..e0a2db3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Index.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ANNOTATION") + */ +final class Index implements Annotation +{ + /** @var string */ + public $name; + /** @var array */ + public $columns; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/InheritanceType.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/InheritanceType.php new file mode 100644 index 0000000..009f3ab --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/InheritanceType.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class InheritanceType implements Annotation +{ + /** @var string */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumn.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumn.php new file mode 100644 index 0000000..bf5c51d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumn.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target({"PROPERTY","ANNOTATION"}) + */ +final class JoinColumn implements Annotation +{ + /** @var string */ + public $name; + /** @var string */ + public $referencedColumnName = 'id'; + /** @var boolean */ + public $unique = false; + /** @var boolean */ + public $nullable = true; + /** @var mixed */ + public $onDelete; + /** @var string */ + public $columnDefinition; + /** @var string */ + public $fieldName; // field name used in non-object hydration (array/scalar) +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumns.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumns.php new file mode 100644 index 0000000..525105f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumns.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class JoinColumns implements Annotation +{ + /** @var array<\Doctrine\ORM\Mapping\JoinColumn> */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinTable.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinTable.php new file mode 100644 index 0000000..9ff9d45 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinTable.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class JoinTable implements Annotation +{ + /** @var string */ + public $name; + /** @var string */ + public $schema; + /** @var array<\Doctrine\ORM\Mapping\JoinColumn> */ + public $joinColumns = array(); + /** @var array<\Doctrine\ORM\Mapping\JoinColumn> */ + public $inverseJoinColumns = array(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToMany.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToMany.php new file mode 100644 index 0000000..28f41aa --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToMany.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class ManyToMany implements Annotation +{ + /** @var string */ + public $targetEntity; + /** @var string */ + public $mappedBy; + /** @var string */ + public $inversedBy; + /** @var array */ + public $cascade; + /** @var string */ + public $fetch = 'LAZY'; + /** @var boolean */ + public $orphanRemoval = false; + /** @var string */ + public $indexBy; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToOne.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToOne.php new file mode 100644 index 0000000..1bc8376 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToOne.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class ManyToOne implements Annotation +{ + /** @var string */ + public $targetEntity; + /** @var array */ + public $cascade; + /** @var string */ + public $fetch = 'LAZY'; + /** @var string */ + public $inversedBy; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappedSuperclass.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappedSuperclass.php new file mode 100644 index 0000000..639d216 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappedSuperclass.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class MappedSuperclass implements Annotation +{ + /** @var string */ + public $repositoryClass; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php new file mode 100644 index 0000000..c71c2e9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php @@ -0,0 +1,332 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * A MappingException indicates that something is wrong with the mapping setup. + * + * @since 2.0 + */ +class MappingException extends \Doctrine\ORM\ORMException +{ + public static function pathRequired() + { + return new self("Specifying the paths to your entities is required ". + "in the AnnotationDriver to retrieve all class names."); + } + + public static function identifierRequired($entityName) + { + return new self("No identifier/primary key specified for Entity '$entityName'." + . " Every Entity must have an identifier/primary key."); + } + + public static function invalidInheritanceType($entityName, $type) + { + return new self("The inheritance type '$type' specified for '$entityName' does not exist."); + } + + public static function generatorNotAllowedWithCompositeId() + { + return new self("Id generators can't be used with a composite id."); + } + + public static function missingFieldName($entity) + { + return new self("The field or association mapping misses the 'fieldName' attribute in entity '$entity'."); + } + + public static function missingTargetEntity($fieldName) + { + return new self("The association mapping '$fieldName' misses the 'targetEntity' attribute."); + } + + public static function missingSourceEntity($fieldName) + { + return new self("The association mapping '$fieldName' misses the 'sourceEntity' attribute."); + } + + public static function mappingFileNotFound($entityName, $fileName) + { + return new self("No mapping file found named '$fileName' for class '$entityName'."); + } + + public static function invalidMappingFile($entityName, $fileName) + { + return new self("Invalid mapping file '$fileName' for class '$entityName'."); + } + + public static function mappingNotFound($className, $fieldName) + { + return new self("No mapping found for field '$fieldName' on class '$className'."); + } + + public static function queryNotFound($className, $queryName) + { + return new self("No query found named '$queryName' on class '$className'."); + } + + public static function oneToManyRequiresMappedBy($fieldName) + { + return new self("OneToMany mapping on field '$fieldName' requires the 'mappedBy' attribute."); + } + + public static function joinTableRequired($fieldName) + { + return new self("The mapping of field '$fieldName' requires an the 'joinTable' attribute."); + } + + /** + * Called if a required option was not found but is required + * + * @param string $field which field cannot be processed? + * @param string $expectedOption which option is required + * @param string $hint Can optionally be used to supply a tip for common mistakes, + * e.g. "Did you think of the plural s?" + * @return MappingException + */ + static function missingRequiredOption($field, $expectedOption, $hint = '') + { + $message = "The mapping of field '{$field}' is invalid: The option '{$expectedOption}' is required."; + + if ( ! empty($hint)) { + $message .= ' (Hint: ' . $hint . ')'; + } + + return new self($message); + } + + /** + * Generic exception for invalid mappings. + * + * @param string $fieldName + */ + public static function invalidMapping($fieldName) + { + return new self("The mapping of field '$fieldName' is invalid."); + } + + /** + * Exception for reflection exceptions - adds the entity name, + * because there might be long classnames that will be shortened + * within the stacktrace + * + * @param string $entity The entity's name + * @param \ReflectionException $previousException + */ + public static function reflectionFailure($entity, \ReflectionException $previousException) + { + return new self('An error occurred in ' . $entity, 0, $previousException); + } + + public static function joinColumnMustPointToMappedField($className, $joinColumn) + { + return new self('The column ' . $joinColumn . ' must be mapped to a field in class ' + . $className . ' since it is referenced by a join column of another class.'); + } + + public static function classIsNotAValidEntityOrMappedSuperClass($className) + { + return new self('Class '.$className.' is not a valid entity or mapped super class.'); + } + + public static function propertyTypeIsRequired($className, $propertyName) + { + return new self("The attribute 'type' is required for the column description of property ".$className."::\$".$propertyName."."); + } + + public static function tableIdGeneratorNotImplemented($className) + { + return new self("TableIdGenerator is not yet implemented for use with class ".$className); + } + + /** + * + * @param string $entity The entity's name + * @param string $fieldName The name of the field that was already declared + */ + public static function duplicateFieldMapping($entity, $fieldName) { + return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + public static function duplicateAssociationMapping($entity, $fieldName) { + return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + public static function duplicateQueryMapping($entity, $queryName) { + return new self('Query named "'.$queryName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + public static function singleIdNotAllowedOnCompositePrimaryKey($entity) { + return new self('Single id is not allowed on composite primary key in entity '.$entity); + } + + public static function unsupportedOptimisticLockingType($entity, $fieldName, $unsupportedType) { + return new self('Locking type "'.$unsupportedType.'" (specified in "'.$entity.'", field "'.$fieldName.'") ' + .'is not supported by Doctrine.' + ); + } + + public static function fileMappingDriversRequireConfiguredDirectoryPath($path = null) + { + if ( ! empty($path)) { + $path = '[' . $path . ']'; + } + + return new self( + 'File mapping drivers must have a valid directory path, ' . + 'however the given path ' . $path . ' seems to be incorrect!' + ); + } + + /** + * Throws an exception that indicates that a class used in a discriminator map does not exist. + * An example would be an outdated (maybe renamed) classname. + * + * @param string $className The class that could not be found + * @param string $owningClass The class that declares the discriminator map. + * @return self + */ + public static function invalidClassInDiscriminatorMap($className, $owningClass) { + return new self( + "Entity class '$className' used in the discriminator map of class '$owningClass' ". + "does not exist." + ); + } + + public static function missingDiscriminatorMap($className) + { + return new self("Entity class '$className' is using inheritance but no discriminator map was defined."); + } + + public static function missingDiscriminatorColumn($className) + { + return new self("Entity class '$className' is using inheritance but no discriminator column was defined."); + } + + public static function invalidDiscriminatorColumnType($className, $type) + { + return new self("Discriminator column type on entity class '$className' is not allowed to be '$type'. 'string' or 'integer' type variables are suggested!"); + } + + public static function cannotVersionIdField($className, $fieldName) + { + return new self("Setting Id field '$fieldName' as versionale in entity class '$className' is not supported."); + } + + public static function sqlConversionNotAllowedForIdentifiers($className, $fieldName, $type) + { + return new self("It is not possible to set id field '$fieldName' to type '$type' in entity class '$className'. The type '$type' requires conversion SQL which is not allowed for identifiers."); + } + + /** + * @param string $className + * @param string $columnName + * @return self + */ + public static function duplicateColumnName($className, $columnName) + { + return new self("Duplicate definition of column '".$columnName."' on entity '".$className."' in a field or discriminator column mapping."); + } + + public static function illegalToManyAssocationOnMappedSuperclass($className, $field) + { + return new self("It is illegal to put an inverse side one-to-many or many-to-many association on mapped superclass '".$className."#".$field."'."); + } + + /** + * @param string $className + * @param string $targetEntity + * @param string $targetField + * @return self + */ + public static function cannotMapCompositePrimaryKeyEntitiesAsForeignId($className, $targetEntity, $targetField) + { + return new self("It is not possible to map entity '".$className."' with a composite primary key ". + "as part of the primary key of another entity '".$targetEntity."#".$targetField."'."); + } + + public static function noSingleAssociationJoinColumnFound($className, $field) + { + return new self("'$className#$field' is not an association with a single join column."); + } + + public static function noFieldNameFoundForColumn($className, $column) + { + return new self("Cannot find a field on '$className' that is mapped to column '$column'. Either the ". + "field does not exist or an association exists but it has multiple join columns."); + } + + public static function illegalOrphanRemovalOnIdentifierAssociation($className, $field) + { + return new self("The orphan removal option is not allowed on an association that is ". + "part of the identifier in '$className#$field'."); + } + + public static function illegalOrphanRemoval($className, $field) + { + return new self("Orphan removal is only allowed on one-to-one and one-to-many ". + "associations, but " . $className."#" .$field . " is not."); + } + + public static function illegalInverseIdentifierAssocation($className, $field) + { + return new self("An inverse association is not allowed to be identifier in '$className#$field'."); + } + + public static function illegalToManyIdentifierAssoaction($className, $field) + { + return new self("Many-to-many or one-to-many associations are not allowed to be identifier in '$className#$field'."); + } + + public static function noInheritanceOnMappedSuperClass($className) + { + return new self("Its not supported to define inheritance information on a mapped superclass '" . $className . "'."); + } + + public static function mappedClassNotPartOfDiscriminatorMap($className, $rootClassName) + { + return new self( + "Entity '" . $className . "' has to be part of the discriminator map of '" . $rootClassName . "' " . + "to be properly mapped in the inheritance hierachy. Alternatively you can make '".$className."' an abstract class " . + "to avoid this exception from occuring." + ); + } + + public static function lifecycleCallbackMethodNotFound($className, $methodName) + { + return new self("Entity '" . $className . "' has no method '" . $methodName . "' to be registered as lifecycle callback."); + } + + public static function invalidFetchMode($className, $annotation) + { + return new self("Entity '" . $className . "' has a mapping with invalid fetch mode '" . $annotation . "'"); + } + + public static function compositeKeyAssignedIdGeneratorRequired($className) + { + return new self("Entity '". $className . "' has a composite identifier but uses an ID generator other than manually assigning (Identity, Sequence). This is not supported."); + } + + public static function invalidTargetEntityClass($targetEntity, $sourceEntity, $associationName) + { + return new self("The target-entity " . $targetEntity . " cannot be found in '" . $sourceEntity."#".$associationName."'."); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQueries.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQueries.php new file mode 100644 index 0000000..1ab2bf2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQueries.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class NamedQueries implements Annotation +{ + /** @var array<\Doctrine\ORM\Mapping\NamedQuery> */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQuery.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQuery.php new file mode 100644 index 0000000..656a4ea --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQuery.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ANNOTATION") + */ +final class NamedQuery implements Annotation +{ + /** @var string */ + public $name; + /** @var string */ + public $query; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToMany.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToMany.php new file mode 100644 index 0000000..b4d8ad3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToMany.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class OneToMany implements Annotation +{ + /** @var string */ + public $mappedBy; + /** @var string */ + public $targetEntity; + /** @var array */ + public $cascade; + /** @var string */ + public $fetch = 'LAZY'; + /** @var boolean */ + public $orphanRemoval = false; + /** @var string */ + public $indexBy; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToOne.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToOne.php new file mode 100644 index 0000000..4baa121 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToOne.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class OneToOne implements Annotation +{ + /** @var string */ + public $targetEntity; + /** @var string */ + public $mappedBy; + /** @var string */ + public $inversedBy; + /** @var array */ + public $cascade; + /** @var string */ + public $fetch = 'LAZY'; + /** @var boolean */ + public $orphanRemoval = false; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OrderBy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OrderBy.php new file mode 100644 index 0000000..c28f110 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OrderBy.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class OrderBy implements Annotation +{ + /** @var array */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostLoad.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostLoad.php new file mode 100644 index 0000000..169bb49 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostLoad.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PostLoad implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostPersist.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostPersist.php new file mode 100644 index 0000000..5b5baed --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostPersist.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PostPersist implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostRemove.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostRemove.php new file mode 100644 index 0000000..4798e26 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostRemove.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PostRemove implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostUpdate.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostUpdate.php new file mode 100644 index 0000000..f7e7539 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostUpdate.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PostUpdate implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreFlush.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreFlush.php new file mode 100644 index 0000000..f5eb086 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreFlush.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PreFlush implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PrePersist.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PrePersist.php new file mode 100644 index 0000000..c382700 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PrePersist.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PrePersist implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreRemove.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreRemove.php new file mode 100644 index 0000000..adcb837 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreRemove.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PreRemove implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreUpdate.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreUpdate.php new file mode 100644 index 0000000..2afd381 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreUpdate.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PreUpdate implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SequenceGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SequenceGenerator.php new file mode 100644 index 0000000..be3d700 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SequenceGenerator.php @@ -0,0 +1,34 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class SequenceGenerator implements Annotation +{ + /** @var string */ + public $sequenceName; + /** @var integer */ + public $allocationSize = 1; + /** @var integer */ + public $initialValue = 1; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Table.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Table.php new file mode 100644 index 0000000..41db294 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Table.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class Table implements Annotation +{ + /** @var string */ + public $name; + /** @var string */ + public $schema; + /** @var array<\Doctrine\ORM\Mapping\Index> */ + public $indexes; + /** @var array<\Doctrine\ORM\Mapping\UniqueConstraint> */ + public $uniqueConstraints; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UniqueConstraint.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UniqueConstraint.php new file mode 100644 index 0000000..db9f8fd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UniqueConstraint.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ANNOTATION") + */ +final class UniqueConstraint implements Annotation +{ + /** @var string */ + public $name; + /** @var array */ + public $columns; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Version.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Version.php new file mode 100644 index 0000000..313e751 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Version.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class Version implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/NativeQuery.php b/vendor/doctrine/orm/lib/Doctrine/ORM/NativeQuery.php new file mode 100644 index 0000000..2ab8744 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NativeQuery.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Represents a native SQL query. + * + * @author Roman Borschel + * @since 2.0 + */ +final class NativeQuery extends AbstractQuery +{ + private $_sql; + + /** + * Sets the SQL of the query. + * + * @param string $sql + * @return NativeQuery This query instance. + */ + public function setSQL($sql) + { + $this->_sql = $sql; + + return $this; + } + + /** + * Gets the SQL query. + * + * @return mixed The built SQL query or an array of all SQL queries. + * @override + */ + public function getSQL() + { + return $this->_sql; + } + + /** + * {@inheritdoc} + */ + protected function _doExecute() + { + $params = $this->_params; + $types = $this->_paramTypes; + + if ($params && is_int(key($params))) { + ksort($params); + ksort($types); + + $params = array_values($params); + $types = array_values($types); + } + + return $this->_em->getConnection()->executeQuery( + $this->_sql, $params, $types, $this->_queryCacheProfile + ); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/NoResultException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/NoResultException.php new file mode 100644 index 0000000..eb31f7c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NoResultException.php @@ -0,0 +1,34 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception thrown when an ORM query unexpectedly does not return any results. + * + * @author robo + * @since 2.0 + */ +class NoResultException extends ORMException +{ + public function __construct() + { + parent::__construct('No result was found for query although at least one row was expected.'); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/NonUniqueResultException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/NonUniqueResultException.php new file mode 100644 index 0000000..1a3a8b7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NonUniqueResultException.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception thrown when an ORM query unexpectedly returns more than one result. + * + * @author robo + * @since 2.0 + */ +class NonUniqueResultException extends ORMException {} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/ORMException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMException.php new file mode 100644 index 0000000..bd16839 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMException.php @@ -0,0 +1,152 @@ +. + */ + +namespace Doctrine\ORM; + +use Exception; + +/** + * Base exception class for all ORM exceptions. + * + * @author Roman Borschel + * @since 2.0 + */ +class ORMException extends Exception +{ + public static function missingMappingDriverImpl() + { + return new self("It's a requirement to specify a Metadata Driver and pass it ". + "to Doctrine\ORM\Configuration::setMetadataDriverImpl()."); + } + + public static function entityMissingForeignAssignedId($entity, $relatedEntity) + { + return new self( + "Entity of type " . get_class($entity) . " has identity through a foreign entity " . get_class($relatedEntity) . ", " . + "however this entity has no identity itself. You have to call EntityManager#persist() on the related entity " . + "and make sure that an identifier was generated before trying to persist '" . get_class($entity) . "'. In case " . + "of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you have to call " . + "EntityManager#flush() between both persist operations." + ); + } + + public static function entityMissingAssignedIdForField($entity, $field) + { + return new self("Entity of type " . get_class($entity) . " is missing an assigned ID for field '" . $field . "'. " . + "The identifier generation strategy for this entity requires the ID field to be populated before ". + "EntityManager#persist() is called. If you want automatically generated identifiers instead " . + "you need to adjust the metadata mapping accordingly." + ); + } + public static function unrecognizedField($field) + { + return new self("Unrecognized field: $field"); + } + + /** + * @param string $className + * @param string $field + */ + public static function invalidOrientation($className, $field) + { + return new self("Invalid order by orientation specified for " . $className . "#" . $field); + } + + public static function invalidFlushMode($mode) + { + return new self("'$mode' is an invalid flush mode."); + } + + public static function entityManagerClosed() + { + return new self("The EntityManager is closed."); + } + + public static function invalidHydrationMode($mode) + { + return new self("'$mode' is an invalid hydration mode."); + } + + public static function mismatchedEventManager() + { + return new self("Cannot use different EventManager instances for EntityManager and Connection."); + } + + public static function findByRequiresParameter($methodName) + { + return new self("You need to pass a parameter to '".$methodName."'"); + } + + public static function invalidFindByCall($entityName, $fieldName, $method) + { + return new self( + "Entity '".$entityName."' has no field '".$fieldName."'. ". + "You can therefore not call '".$method."' on the entities' repository" + ); + } + + public static function invalidFindByInverseAssociation($entityName, $associationFieldName) + { + return new self( + "You cannot search for the association field '".$entityName."#".$associationFieldName."', ". + "because it is the inverse side of an association. Find methods only work on owning side associations." + ); + } + + public static function invalidResultCacheDriver() { + return new self("Invalid result cache driver; it must implement \Doctrine\Common\Cache\Cache."); + } + + public static function notSupported() { + return new self("This behaviour is (currently) not supported by Doctrine 2"); + } + + public static function queryCacheNotConfigured() + { + return new self('Query Cache is not configured.'); + } + + public static function metadataCacheNotConfigured() + { + return new self('Class Metadata Cache is not configured.'); + } + + public static function proxyClassesAlwaysRegenerating() + { + return new self('Proxy Classes are always regenerating.'); + } + + public static function unknownEntityNamespace($entityNamespaceAlias) + { + return new self( + "Unknown Entity namespace alias '$entityNamespaceAlias'." + ); + } + + public static function invalidEntityRepository($className) + { + return new self("Invalid repository class '".$className."'. ". + "it must be a Doctrine\ORM\EntityRepository."); + } + + public static function missingIdentifierField($className, $fieldName) + { + return new self("The identifier $fieldName is missing for a query of " . $className); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php new file mode 100644 index 0000000..878ee4b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Contains exception messages for all invalid lifecycle state exceptions inside UnitOfWork + * + * @author Benjamin Eberlei + */ +class ORMInvalidArgumentException extends \InvalidArgumentException +{ + static public function scheduleInsertForManagedEntity($entity) + { + return new self("A managed+dirty entity " . self::objToStr($entity) . " can not be scheduled for insertion."); + } + + static public function scheduleInsertForRemovedEntity($entity) + { + return new self("Removed entity " . self::objToStr($entity) . " can not be scheduled for insertion."); + } + + static public function scheduleInsertTwice($entity) + { + return new self("Entity " . self::objToStr($entity) . " can not be scheduled for insertion twice."); + } + + static public function entityWithoutIdentity($className, $entity) + { + throw new self( + "The given entity of type '" . $className . "' (".self::objToStr($entity).") has no identity/no " . + "id values set. It cannot be added to the identity map." + ); + } + + static public function readOnlyRequiresManagedEntity($entity) + { + return new self("Only managed entities can be marked or checked as read only. But " . self::objToStr($entity) . " is not"); + } + + static public function newEntityFoundThroughRelationship(array $assoc, $entry) + { + return new self("A new entity was found through the relationship '" + . $assoc['sourceEntity'] . "#" . $assoc['fieldName'] . "' that was not" + . " configured to cascade persist operations for entity: " . self::objToStr($entry) . "." + . " To solve this issue: Either explicitly call EntityManager#persist()" + . " on this unknown entity or configure cascade persist " + . " this association in the mapping for example @ManyToOne(..,cascade={\"persist\"}). " + . " If you cannot find out which entity causes the problem" + . " implement '" . $assoc['targetEntity'] . "#__toString()' to get a clue."); + } + + static public function detachedEntityFoundThroughRelationship(array $assoc, $entry) + { + throw new self("A detached entity of type " . $assoc['targetEntity'] . " (" . self::objToStr($entry) . ") " + . " was found through the relationship '" . $assoc['sourceEntity'] . "#" . $assoc['fieldName'] . "' " + . "during cascading a persist operation."); + } + + static public function entityNotManaged($entity) + { + throw new self("Entity " . self::objToStr($entity) . " is not managed. An entity is managed if its fetched " . + "from the database or registered as new through EntityManager#persist"); + } + + static public function entityHasNoIdentity($entity, $operation) + { + throw new self("Entity has no identity, therefore " . $operation ." cannot be performed. " . self::objToStr($entity)); + } + + static public function entityIsRemoved($entity, $operation) + { + throw new self("Entity is removed, therefore " . $operation ." cannot be performed. " . self::objToStr($entity)); + } + + static public function detachedEntityCannot($entity, $operation) + { + throw new self("A detached entity was found during " . $operation . " " . self::objToStr($entity)); + } + + /** + * Helper method to show an object as string. + * + * @param object $obj + * @return string + */ + private static function objToStr($obj) + { + return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/OptimisticLockException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/OptimisticLockException.php new file mode 100644 index 0000000..9b5147a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/OptimisticLockException.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * An OptimisticLockException is thrown when a version check on an object + * that uses optimistic locking through a version field fails. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +class OptimisticLockException extends ORMException +{ + private $entity; + + public function __construct($msg, $entity) + { + parent::__construct($msg); + $this->entity = $entity; + } + + /** + * Gets the entity that caused the exception. + * + * @return object + */ + public function getEntity() + { + return $this->entity; + } + + public static function lockFailed($entity) + { + return new self("The optimistic lock on an entity failed.", $entity); + } + + public static function lockFailedVersionMissmatch($entity, $expectedLockVersion, $actualLockVersion) + { + return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity); + } + + public static function notVersioned($entityName) + { + return new self("Cannot obtain optimistic lock on unversioned entity " . $entityName, null); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php b/vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php new file mode 100644 index 0000000..4c42c29 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php @@ -0,0 +1,792 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\Common\Collections\Collection, + Doctrine\Common\Collections\ArrayCollection, + Closure; + +/** + * A PersistentCollection represents a collection of elements that have persistent state. + * + * Collections of entities represent only the associations (links) to those entities. + * That means, if the collection is part of a many-many mapping and you remove + * entities from the collection, only the links in the relation table are removed (on flush). + * Similarly, if you remove entities from a collection that is part of a one-many + * mapping this will only result in the nulling out of the foreign keys on flush. + * + * @since 2.0 + * @author Konsta Vesterinen + * @author Roman Borschel + * @author Giorgio Sironi + * @todo Design for inheritance to allow custom implementations? + */ +final class PersistentCollection implements Collection +{ + /** + * A snapshot of the collection at the moment it was fetched from the database. + * This is used to create a diff of the collection at commit time. + * + * @var array + */ + private $snapshot = array(); + + /** + * The entity that owns this collection. + * + * @var object + */ + private $owner; + + /** + * The association mapping the collection belongs to. + * This is currently either a OneToManyMapping or a ManyToManyMapping. + * + * @var array + */ + private $association; + + /** + * The EntityManager that manages the persistence of the collection. + * + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * The name of the field on the target entities that points to the owner + * of the collection. This is only set if the association is bi-directional. + * + * @var string + */ + private $backRefFieldName; + + /** + * The class descriptor of the collection's entity type. + */ + private $typeClass; + + /** + * Whether the collection is dirty and needs to be synchronized with the database + * when the UnitOfWork that manages its persistent state commits. + * + * @var boolean + */ + private $isDirty = false; + + /** + * Whether the collection has already been initialized. + * + * @var boolean + */ + private $initialized = true; + + /** + * The wrapped Collection instance. + * + * @var Collection + */ + private $coll; + + /** + * Creates a new persistent collection. + * + * @param EntityManager $em The EntityManager the collection will be associated with. + * @param ClassMetadata $class The class descriptor of the entity type of this collection. + * @param array The collection elements. + */ + public function __construct(EntityManager $em, $class, $coll) + { + $this->coll = $coll; + $this->em = $em; + $this->typeClass = $class; + } + + /** + * INTERNAL: + * Sets the collection's owning entity together with the AssociationMapping that + * describes the association between the owner and the elements of the collection. + * + * @param object $entity + * @param AssociationMapping $assoc + */ + public function setOwner($entity, array $assoc) + { + $this->owner = $entity; + $this->association = $assoc; + $this->backRefFieldName = $assoc['inversedBy'] ?: $assoc['mappedBy']; + } + + /** + * INTERNAL: + * Gets the collection owner. + * + * @return object + */ + public function getOwner() + { + return $this->owner; + } + + public function getTypeClass() + { + return $this->typeClass; + } + + /** + * INTERNAL: + * Adds an element to a collection during hydration. This will automatically + * complete bidirectional associations in the case of a one-to-many association. + * + * @param mixed $element The element to add. + */ + public function hydrateAdd($element) + { + $this->coll->add($element); + + // If _backRefFieldName is set and its a one-to-many association, + // we need to set the back reference. + if ($this->backRefFieldName && $this->association['type'] === ClassMetadata::ONE_TO_MANY) { + // Set back reference to owner + $this->typeClass->reflFields[$this->backRefFieldName]->setValue( + $element, $this->owner + ); + + $this->em->getUnitOfWork()->setOriginalEntityProperty( + spl_object_hash($element), $this->backRefFieldName, $this->owner + ); + } + } + + /** + * INTERNAL: + * Sets a keyed element in the collection during hydration. + * + * @param mixed $key The key to set. + * $param mixed $value The element to set. + */ + public function hydrateSet($key, $element) + { + $this->coll->set($key, $element); + + // If _backRefFieldName is set, then the association is bidirectional + // and we need to set the back reference. + if ($this->backRefFieldName && $this->association['type'] === ClassMetadata::ONE_TO_MANY) { + // Set back reference to owner + $this->typeClass->reflFields[$this->backRefFieldName]->setValue( + $element, $this->owner + ); + } + } + + /** + * Initializes the collection by loading its contents from the database + * if the collection is not yet initialized. + */ + public function initialize() + { + if ($this->initialized || ! $this->association) { + return; + } + + // Has NEW objects added through add(). Remember them. + $newObjects = array(); + + if ($this->isDirty) { + $newObjects = $this->coll->toArray(); + } + + $this->coll->clear(); + $this->em->getUnitOfWork()->loadCollection($this); + $this->takeSnapshot(); + + // Reattach NEW objects added through add(), if any. + if ($newObjects) { + foreach ($newObjects as $obj) { + $this->coll->add($obj); + } + + $this->isDirty = true; + } + + $this->initialized = true; + } + + /** + * INTERNAL: + * Tells this collection to take a snapshot of its current state. + */ + public function takeSnapshot() + { + $this->snapshot = $this->coll->toArray(); + $this->isDirty = false; + } + + /** + * INTERNAL: + * Returns the last snapshot of the elements in the collection. + * + * @return array The last snapshot of the elements. + */ + public function getSnapshot() + { + return $this->snapshot; + } + + /** + * INTERNAL: + * getDeleteDiff + * + * @return array + */ + public function getDeleteDiff() + { + return array_udiff_assoc( + $this->snapshot, + $this->coll->toArray(), + function($a, $b) { return $a === $b ? 0 : 1; } + ); + } + + /** + * INTERNAL: + * getInsertDiff + * + * @return array + */ + public function getInsertDiff() + { + return array_udiff_assoc( + $this->coll->toArray(), + $this->snapshot, + function($a, $b) { return $a === $b ? 0 : 1; } + ); + } + + /** + * INTERNAL: Gets the association mapping of the collection. + * + * @return \Doctrine\ORM\Mapping\AssociationMapping + */ + public function getMapping() + { + return $this->association; + } + + /** + * Marks this collection as changed/dirty. + */ + private function changed() + { + if ($this->isDirty) { + return; + } + + $this->isDirty = true; + + if ($this->association !== null && + $this->association['isOwningSide'] && + $this->association['type'] === ClassMetadata::MANY_TO_MANY && + $this->owner && + $this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) { + $this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner); + } + } + + /** + * Gets a boolean flag indicating whether this collection is dirty which means + * its state needs to be synchronized with the database. + * + * @return boolean TRUE if the collection is dirty, FALSE otherwise. + */ + public function isDirty() + { + return $this->isDirty; + } + + /** + * Sets a boolean flag, indicating whether this collection is dirty. + * + * @param boolean $dirty Whether the collection should be marked dirty or not. + */ + public function setDirty($dirty) + { + $this->isDirty = $dirty; + } + + /** + * Sets the initialized flag of the collection, forcing it into that state. + * + * @param boolean $bool + */ + public function setInitialized($bool) + { + $this->initialized = $bool; + } + + /** + * Checks whether this collection has been initialized. + * + * @return boolean + */ + public function isInitialized() + { + return $this->initialized; + } + + /** {@inheritdoc} */ + public function first() + { + $this->initialize(); + + return $this->coll->first(); + } + + /** {@inheritdoc} */ + public function last() + { + $this->initialize(); + + return $this->coll->last(); + } + + /** + * {@inheritdoc} + */ + public function remove($key) + { + // TODO: If the keys are persistent as well (not yet implemented) + // and the collection is not initialized and orphanRemoval is + // not used we can issue a straight SQL delete/update on the + // association (table). Without initializing the collection. + $this->initialize(); + + $removed = $this->coll->remove($key); + + if ( ! $removed) { + return $removed; + } + + $this->changed(); + + if ($this->association !== null && + $this->association['type'] & ClassMetadata::TO_MANY && + $this->owner && + $this->association['orphanRemoval']) { + $this->em->getUnitOfWork()->scheduleOrphanRemoval($removed); + } + + return $removed; + } + + /** + * {@inheritdoc} + */ + public function removeElement($element) + { + if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { + if ($this->coll->contains($element)) { + return $this->coll->removeElement($element); + } + + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + if ($persister->removeElement($this, $element)) { + return $element; + } + + return null; + } + + $this->initialize(); + + $removed = $this->coll->removeElement($element); + + if ( ! $removed) { + return $removed; + } + + $this->changed(); + + if ($this->association !== null && + $this->association['type'] & ClassMetadata::TO_MANY && + $this->owner && + $this->association['orphanRemoval']) { + $this->em->getUnitOfWork()->scheduleOrphanRemoval($element); + } + + return $removed; + } + + /** + * {@inheritdoc} + */ + public function containsKey($key) + { + $this->initialize(); + + return $this->coll->containsKey($key); + } + + /** + * {@inheritdoc} + */ + public function contains($element) + { + if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + return $this->coll->contains($element) || $persister->contains($this, $element); + } + + $this->initialize(); + + return $this->coll->contains($element); + } + + /** + * {@inheritdoc} + */ + public function exists(Closure $p) + { + $this->initialize(); + + return $this->coll->exists($p); + } + + /** + * {@inheritdoc} + */ + public function indexOf($element) + { + $this->initialize(); + + return $this->coll->indexOf($element); + } + + /** + * {@inheritdoc} + */ + public function get($key) + { + $this->initialize(); + + return $this->coll->get($key); + } + + /** + * {@inheritdoc} + */ + public function getKeys() + { + $this->initialize(); + + return $this->coll->getKeys(); + } + + /** + * {@inheritdoc} + */ + public function getValues() + { + $this->initialize(); + + return $this->coll->getValues(); + } + + /** + * {@inheritdoc} + */ + public function count() + { + if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + return $persister->count($this) + ($this->isDirty ? $this->coll->count() : 0); + } + + $this->initialize(); + + return $this->coll->count(); + } + + /** + * {@inheritdoc} + */ + public function set($key, $value) + { + $this->initialize(); + + $this->coll->set($key, $value); + + $this->changed(); + } + + /** + * {@inheritdoc} + */ + public function add($value) + { + $this->coll->add($value); + + $this->changed(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function isEmpty() + { + $this->initialize(); + + return $this->coll->isEmpty(); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $this->initialize(); + + return $this->coll->getIterator(); + } + + /** + * {@inheritdoc} + */ + public function map(Closure $func) + { + $this->initialize(); + + return $this->coll->map($func); + } + + /** + * {@inheritdoc} + */ + public function filter(Closure $p) + { + $this->initialize(); + + return $this->coll->filter($p); + } + + /** + * {@inheritdoc} + */ + public function forAll(Closure $p) + { + $this->initialize(); + + return $this->coll->forAll($p); + } + + /** + * {@inheritdoc} + */ + public function partition(Closure $p) + { + $this->initialize(); + + return $this->coll->partition($p); + } + + /** + * {@inheritdoc} + */ + public function toArray() + { + $this->initialize(); + + return $this->coll->toArray(); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + if ($this->initialized && $this->isEmpty()) { + return; + } + + $uow = $this->em->getUnitOfWork(); + + if ($this->association['type'] & ClassMetadata::TO_MANY && + $this->association['orphanRemoval'] && + $this->owner) { + // we need to initialize here, as orphan removal acts like implicit cascadeRemove, + // hence for event listeners we need the objects in memory. + $this->initialize(); + + foreach ($this->coll as $element) { + $uow->scheduleOrphanRemoval($element); + } + } + + $this->coll->clear(); + + $this->initialized = true; // direct call, {@link initialize()} is too expensive + + if ($this->association['isOwningSide']) { + $this->changed(); + + $uow->scheduleCollectionDeletion($this); + + $this->takeSnapshot(); + } + } + + /** + * Called by PHP when this collection is serialized. Ensures that only the + * elements are properly serialized. + * + * @internal Tried to implement Serializable first but that did not work well + * with circular references. This solution seems simpler and works well. + */ + public function __sleep() + { + return array('coll', 'initialized'); + } + + /* ArrayAccess implementation */ + + /** + * @see containsKey() + */ + public function offsetExists($offset) + { + return $this->containsKey($offset); + } + + /** + * @see get() + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * @see add() + * @see set() + */ + public function offsetSet($offset, $value) + { + if ( ! isset($offset)) { + return $this->add($value); + } + + return $this->set($offset, $value); + } + + /** + * @see remove() + */ + public function offsetUnset($offset) + { + return $this->remove($offset); + } + + public function key() + { + return $this->coll->key(); + } + + /** + * Gets the element of the collection at the current iterator position. + */ + public function current() + { + return $this->coll->current(); + } + + /** + * Moves the internal iterator position to the next element. + */ + public function next() + { + return $this->coll->next(); + } + + /** + * Retrieves the wrapped Collection instance. + * + * @return \Doctrine\Common\Collections\Collection + */ + public function unwrap() + { + return $this->coll; + } + + /** + * Extract a slice of $length elements starting at position $offset from the Collection. + * + * If $length is null it returns all elements from $offset to the end of the Collection. + * Keys have to be preserved by this method. Calling this method will only return the + * selected slice and NOT change the elements contained in the collection slice is called on. + * + * @param int $offset + * @param int $length + * + * @return array + */ + public function slice($offset, $length = null) + { + if ( ! $this->initialized && ! $this->isDirty && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + return $persister->slice($this, $offset, $length); + } + + $this->initialize(); + + return $this->coll->slice($offset, $length); + } + + /** + * Cleanup internal state of cloned persistent collection. + * + * The following problems have to be prevented: + * 1. Added entities are added to old PC + * 2. New collection is not dirty, if reused on other entity nothing + * changes. + * 3. Snapshot leads to invalid diffs being generated. + * 4. Lazy loading grabs entities from old owner object. + * 5. New collection is connected to old owner and leads to duplicate keys. + */ + public function __clone() + { + if (is_object($this->coll)) { + $this->coll = clone $this->coll; + } + + $this->initialize(); + $this->owner = null; + + $this->snapshot = array(); + + $this->changed(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php new file mode 100644 index 0000000..8c12948 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php @@ -0,0 +1,207 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\EntityManager, + Doctrine\ORM\PersistentCollection; + +/** + * Base class for all collection persisters. + * + * @since 2.0 + * @author Roman Borschel + */ +abstract class AbstractCollectionPersister +{ + /** + * @var EntityManager + */ + protected $_em; + + /** + * @var \Doctrine\DBAL\Connection + */ + protected $_conn; + + /** + * @var \Doctrine\ORM\UnitOfWork + */ + protected $_uow; + + /** + * Initializes a new instance of a class derived from AbstractCollectionPersister. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->_em = $em; + $this->_uow = $em->getUnitOfWork(); + $this->_conn = $em->getConnection(); + } + + /** + * Deletes the persistent state represented by the given collection. + * + * @param PersistentCollection $coll + */ + public function delete(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + + if ( ! $mapping['isOwningSide']) { + return; // ignore inverse side + } + + $sql = $this->_getDeleteSQL($coll); + $this->_conn->executeUpdate($sql, $this->_getDeleteSQLParameters($coll)); + } + + /** + * Gets the SQL statement for deleting the given collection. + * + * @param PersistentCollection $coll + */ + abstract protected function _getDeleteSQL(PersistentCollection $coll); + + /** + * Gets the SQL parameters for the corresponding SQL statement to delete + * the given collection. + * + * @param PersistentCollection $coll + */ + abstract protected function _getDeleteSQLParameters(PersistentCollection $coll); + + /** + * Updates the given collection, synchronizing it's state with the database + * by inserting, updating and deleting individual elements. + * + * @param PersistentCollection $coll + */ + public function update(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + + if ( ! $mapping['isOwningSide']) { + return; // ignore inverse side + } + + $this->deleteRows($coll); + //$this->updateRows($coll); + $this->insertRows($coll); + } + + public function deleteRows(PersistentCollection $coll) + { + $deleteDiff = $coll->getDeleteDiff(); + $sql = $this->_getDeleteRowSQL($coll); + + foreach ($deleteDiff as $element) { + $this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element)); + } + } + + //public function updateRows(PersistentCollection $coll) + //{} + + public function insertRows(PersistentCollection $coll) + { + $insertDiff = $coll->getInsertDiff(); + $sql = $this->_getInsertRowSQL($coll); + + foreach ($insertDiff as $element) { + $this->_conn->executeUpdate($sql, $this->_getInsertRowSQLParameters($coll, $element)); + } + } + + public function count(PersistentCollection $coll) + { + throw new \BadMethodCallException("Counting the size of this persistent collection is not supported by this CollectionPersister."); + } + + public function slice(PersistentCollection $coll, $offset, $length = null) + { + throw new \BadMethodCallException("Slicing elements is not supported by this CollectionPersister."); + } + + public function contains(PersistentCollection $coll, $element) + { + throw new \BadMethodCallException("Checking for existance of an element is not supported by this CollectionPersister."); + } + + public function containsKey(PersistentCollection $coll, $key) + { + throw new \BadMethodCallException("Checking for existance of a key is not supported by this CollectionPersister."); + } + + public function removeElement(PersistentCollection $coll, $element) + { + throw new \BadMethodCallException("Removing an element is not supported by this CollectionPersister."); + } + + public function removeKey(PersistentCollection $coll, $key) + { + throw new \BadMethodCallException("Removing a key is not supported by this CollectionPersister."); + } + + public function get(PersistentCollection $coll, $index) + { + throw new \BadMethodCallException("Selecting a collection by index is not supported by this CollectionPersister."); + } + + /** + * Gets the SQL statement used for deleting a row from the collection. + * + * @param PersistentCollection $coll + */ + abstract protected function _getDeleteRowSQL(PersistentCollection $coll); + + /** + * Gets the SQL parameters for the corresponding SQL statement to delete the given + * element from the given collection. + * + * @param PersistentCollection $coll + * @param mixed $element + */ + abstract protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element); + + /** + * Gets the SQL statement used for updating a row in the collection. + * + * @param PersistentCollection $coll + */ + abstract protected function _getUpdateRowSQL(PersistentCollection $coll); + + /** + * Gets the SQL statement used for inserting a row in the collection. + * + * @param PersistentCollection $coll + */ + abstract protected function _getInsertRowSQL(PersistentCollection $coll); + + /** + * Gets the SQL parameters for the corresponding SQL statement to insert the given + * element of the given collection into the database. + * + * @param PersistentCollection $coll + * @param mixed $element + */ + abstract protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php new file mode 100644 index 0000000..191c077 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/AbstractEntityInheritancePersister.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\DBAL\Types\Type; + +/** + * Base class for entity persisters that implement a certain inheritance mapping strategy. + * All these persisters are assumed to use a discriminator column to discriminate entity + * types in the hierarchy. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +abstract class AbstractEntityInheritancePersister extends BasicEntityPersister +{ + /** + * {@inheritdoc} + */ + protected function _prepareInsertData($entity) + { + $data = parent::_prepareInsertData($entity); + + // Populate the discriminator column + $discColumn = $this->_class->discriminatorColumn; + $this->_columnTypes[$discColumn['name']] = $discColumn['type']; + $data[$this->_getDiscriminatorColumnTableName()][$discColumn['name']] = $this->_class->discriminatorValue; + + return $data; + } + + /** + * Gets the name of the table that contains the discriminator column. + * + * @return string The table name. + */ + abstract protected function _getDiscriminatorColumnTableName(); + + /** + * {@inheritdoc} + */ + protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') + { + $columnName = $class->columnNames[$field]; + $sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $class->getQuotedColumnName($field, $this->_platform); + $columnAlias = $this->getSQLColumnAlias($columnName); + $this->_rsm->addFieldResult($alias, $columnAlias, $field, $class->name); + + if (isset($class->fieldMappings[$field]['requireSQLConversion'])) { + $type = Type::getType($class->getTypeOfField($field)); + $sql = $type->convertToPHPValueSQL($sql, $this->_platform); + } + + return $sql . ' AS ' . $columnAlias; + } + + protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className) + { + $columnAlias = $this->getSQLColumnAlias($joinColumnName); + $this->_rsm->addMetaResult('r', $columnAlias, $joinColumnName); + + return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php new file mode 100644 index 0000000..d3b8fce --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -0,0 +1,1605 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use PDO, + Doctrine\DBAL\LockMode, + Doctrine\DBAL\Types\Type, + Doctrine\DBAL\Connection, + Doctrine\ORM\ORMException, + Doctrine\ORM\OptimisticLockException, + Doctrine\ORM\EntityManager, + Doctrine\ORM\UnitOfWork, + Doctrine\ORM\Query, + Doctrine\ORM\PersistentCollection, + Doctrine\ORM\Mapping\MappingException, + Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\Events, + Doctrine\ORM\Event\LifecycleEventArgs; + +/** + * A BasicEntityPersiter maps an entity to a single table in a relational database. + * + * A persister is always responsible for a single entity type. + * + * EntityPersisters are used during a UnitOfWork to apply any changes to the persistent + * state of entities onto a relational database when the UnitOfWork is committed, + * as well as for basic querying of entities and their associations (not DQL). + * + * The persisting operations that are invoked during a commit of a UnitOfWork to + * persist the persistent entity state are: + * + * - {@link addInsert} : To schedule an entity for insertion. + * - {@link executeInserts} : To execute all scheduled insertions. + * - {@link update} : To update the persistent state of an entity. + * - {@link delete} : To delete the persistent state of an entity. + * + * As can be seen from the above list, insertions are batched and executed all at once + * for increased efficiency. + * + * The querying operations invoked during a UnitOfWork, either through direct find + * requests or lazy-loading, are the following: + * + * - {@link load} : Loads (the state of) a single, managed entity. + * - {@link loadAll} : Loads multiple, managed entities. + * - {@link loadOneToOneEntity} : Loads a one/many-to-one entity association (lazy-loading). + * - {@link loadOneToManyCollection} : Loads a one-to-many entity association (lazy-loading). + * - {@link loadManyToManyCollection} : Loads a many-to-many entity association (lazy-loading). + * + * The BasicEntityPersister implementation provides the default behavior for + * persisting and querying entities that are mapped to a single database table. + * + * Subclasses can be created to provide custom persisting and querying strategies, + * i.e. spanning multiple tables. + * + * @author Roman Borschel + * @author Giorgio Sironi + * @author Benjamin Eberlei + * @author Alexander + * @since 2.0 + */ +class BasicEntityPersister +{ + /** + * Metadata object that describes the mapping of the mapped entity class. + * + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + protected $_class; + + /** + * The underlying DBAL Connection of the used EntityManager. + * + * @var \Doctrine\DBAL\Connection $conn + */ + protected $_conn; + + /** + * The database platform. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $_platform; + + /** + * The EntityManager instance. + * + * @var \Doctrine\ORM\EntityManager + */ + protected $_em; + + /** + * Queued inserts. + * + * @var array + */ + protected $_queuedInserts = array(); + + /** + * ResultSetMapping that is used for all queries. Is generated lazily once per request. + * + * TODO: Evaluate Caching in combination with the other cached SQL snippets. + * + * @var Query\ResultSetMapping + */ + protected $_rsm; + + /** + * The map of column names to DBAL mapping types of all prepared columns used + * when INSERTing or UPDATEing an entity. + * + * @var array + * @see _prepareInsertData($entity) + * @see _prepareUpdateData($entity) + */ + protected $_columnTypes = array(); + + /** + * The INSERT SQL statement used for entities handled by this persister. + * This SQL is only generated once per request, if at all. + * + * @var string + */ + private $_insertSql; + + /** + * The SELECT column list SQL fragment used for querying entities by this persister. + * This SQL fragment is only generated once per request, if at all. + * + * @var string + */ + protected $_selectColumnListSql; + + /** + * The JOIN SQL fragement used to eagerly load all many-to-one and one-to-one + * associations configured as FETCH_EAGER, aswell as all inverse one-to-one associations. + * + * @var string + */ + protected $_selectJoinSql; + + /** + * Counter for creating unique SQL table and column aliases. + * + * @var integer + */ + protected $_sqlAliasCounter = 0; + + /** + * Map from class names (FQCN) to the corresponding generated SQL table aliases. + * + * @var array + */ + protected $_sqlTableAliases = array(); + + /** + * Initializes a new BasicEntityPersister that uses the given EntityManager + * and persists instances of the class described by the given ClassMetadata descriptor. + * + * @param \Doctrine\ORM\EntityManager $em + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + */ + public function __construct(EntityManager $em, ClassMetadata $class) + { + $this->_em = $em; + $this->_class = $class; + $this->_conn = $em->getConnection(); + $this->_platform = $this->_conn->getDatabasePlatform(); + } + + /** + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + public function getClassMetadata() + { + return $this->_class; + } + + /** + * Adds an entity to the queued insertions. + * The entity remains queued until {@link executeInserts} is invoked. + * + * @param object $entity The entity to queue for insertion. + */ + public function addInsert($entity) + { + $this->_queuedInserts[spl_object_hash($entity)] = $entity; + } + + /** + * Executes all queued entity insertions and returns any generated post-insert + * identifiers that were created as a result of the insertions. + * + * If no inserts are queued, invoking this method is a NOOP. + * + * @return array An array of any generated post-insert IDs. This will be an empty array + * if the entity class does not use the IDENTITY generation strategy. + */ + public function executeInserts() + { + if ( ! $this->_queuedInserts) { + return; + } + + $postInsertIds = array(); + $idGen = $this->_class->idGenerator; + $isPostInsertId = $idGen->isPostInsertGenerator(); + + $stmt = $this->_conn->prepare($this->_getInsertSQL()); + $tableName = $this->_class->getTableName(); + + foreach ($this->_queuedInserts as $entity) { + $insertData = $this->_prepareInsertData($entity); + + if (isset($insertData[$tableName])) { + $paramIndex = 1; + + foreach ($insertData[$tableName] as $column => $value) { + $stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$column]); + } + } + + $stmt->execute(); + + if ($isPostInsertId) { + $id = $idGen->generate($this->_em, $entity); + $postInsertIds[$id] = $entity; + } else { + $id = $this->_class->getIdentifierValues($entity); + } + + if ($this->_class->isVersioned) { + $this->assignDefaultVersionValue($entity, $id); + } + } + + $stmt->closeCursor(); + $this->_queuedInserts = array(); + + return $postInsertIds; + } + + /** + * Retrieves the default version value which was created + * by the preceding INSERT statement and assigns it back in to the + * entities version field. + * + * @param object $entity + * @param mixed $id + */ + protected function assignDefaultVersionValue($entity, $id) + { + $value = $this->fetchVersionValue($this->_class, $id); + $this->_class->setFieldValue($entity, $this->_class->versionField, $value); + } + + /** + * Fetch the current version value of a versioned entity. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $versionedClass + * @param mixed $id + * @return mixed + */ + protected function fetchVersionValue($versionedClass, $id) + { + $versionField = $versionedClass->versionField; + $identifier = $versionedClass->getIdentifierColumnNames(); + + $versionFieldColumnName = $versionedClass->getQuotedColumnName($versionField, $this->_platform); + + //FIXME: Order with composite keys might not be correct + $sql = 'SELECT ' . $versionFieldColumnName + . ' FROM ' . $versionedClass->getQuotedTableName($this->_platform) + . ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?'; + $value = $this->_conn->fetchColumn($sql, array_values((array)$id)); + + return Type::getType($versionedClass->fieldMappings[$versionField]['type'])->convertToPHPValue($value, $this->_platform); + } + + /** + * Updates a managed entity. The entity is updated according to its current changeset + * in the running UnitOfWork. If there is no changeset, nothing is updated. + * + * The data to update is retrieved through {@link _prepareUpdateData}. + * Subclasses that override this method are supposed to obtain the update data + * in the same way, through {@link _prepareUpdateData}. + * + * Subclasses are also supposed to take care of versioning when overriding this method, + * if necessary. The {@link _updateTable} method can be used to apply the data retrieved + * from {@_prepareUpdateData} on the target tables, thereby optionally applying versioning. + * + * @param object $entity The entity to update. + */ + public function update($entity) + { + $updateData = $this->_prepareUpdateData($entity); + $tableName = $this->_class->getTableName(); + + if (isset($updateData[$tableName]) && $updateData[$tableName]) { + $this->_updateTable( + $entity, $this->_class->getQuotedTableName($this->_platform), + $updateData[$tableName], $this->_class->isVersioned + ); + + if ($this->_class->isVersioned) { + $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); + $this->assignDefaultVersionValue($entity, $id); + } + } + } + + /** + * Performs an UPDATE statement for an entity on a specific table. + * The UPDATE can optionally be versioned, which requires the entity to have a version field. + * + * @param object $entity The entity object being updated. + * @param string $quotedTableName The quoted name of the table to apply the UPDATE on. + * @param array $updateData The map of columns to update (column => value). + * @param boolean $versioned Whether the UPDATE should be versioned. + */ + protected final function _updateTable($entity, $quotedTableName, array $updateData, $versioned = false) + { + $set = $params = $types = array(); + + foreach ($updateData as $columnName => $value) { + $column = $columnName; + $placeholder = '?'; + + if (isset($this->_class->fieldNames[$columnName])) { + $column = $this->_class->getQuotedColumnName($this->_class->fieldNames[$columnName], $this->_platform); + + if (isset($this->_class->fieldMappings[$this->_class->fieldNames[$columnName]]['requireSQLConversion'])) { + $type = Type::getType($this->_columnTypes[$columnName]); + $placeholder = $type->convertToDatabaseValueSQL('?', $this->_platform); + } + } + + $set[] = $column . ' = ' . $placeholder; + $params[] = $value; + $types[] = $this->_columnTypes[$columnName]; + } + + $where = array(); + $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); + + foreach ($this->_class->identifier as $idField) { + if (isset($this->_class->associationMappings[$idField])) { + $targetMapping = $this->_em->getClassMetadata($this->_class->associationMappings[$idField]['targetEntity']); + $where[] = $this->_class->associationMappings[$idField]['joinColumns'][0]['name']; + $params[] = $id[$idField]; + $types[] = $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type']; + } else { + $where[] = $this->_class->getQuotedColumnName($idField, $this->_platform); + $params[] = $id[$idField]; + $types[] = $this->_class->fieldMappings[$idField]['type']; + } + } + + if ($versioned) { + $versionField = $this->_class->versionField; + $versionFieldType = $this->_class->fieldMappings[$versionField]['type']; + $versionColumn = $this->_class->getQuotedColumnName($versionField, $this->_platform); + + if ($versionFieldType == Type::INTEGER) { + $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1'; + } else if ($versionFieldType == Type::DATETIME) { + $set[] = $versionColumn . ' = CURRENT_TIMESTAMP'; + } + + $where[] = $versionColumn; + $params[] = $this->_class->reflFields[$versionField]->getValue($entity); + $types[] = $this->_class->fieldMappings[$versionField]['type']; + } + + $sql = 'UPDATE ' . $quotedTableName + . ' SET ' . implode(', ', $set) + . ' WHERE ' . implode(' = ? AND ', $where) . ' = ?'; + + $result = $this->_conn->executeUpdate($sql, $params, $types); + + if ($versioned && ! $result) { + throw OptimisticLockException::lockFailed($entity); + } + } + + /** + * @todo Add check for platform if it supports foreign keys/cascading. + * @param array $identifier + * @return void + */ + protected function deleteJoinTableRecords($identifier) + { + foreach ($this->_class->associationMappings as $mapping) { + if ($mapping['type'] == ClassMetadata::MANY_TO_MANY) { + // @Todo this only covers scenarios with no inheritance or of the same level. Is there something + // like self-referential relationship between different levels of an inheritance hierachy? I hope not! + $selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']); + + if ( ! $mapping['isOwningSide']) { + $relatedClass = $this->_em->getClassMetadata($mapping['targetEntity']); + $mapping = $relatedClass->associationMappings[$mapping['mappedBy']]; + $keys = array_keys($mapping['relationToTargetKeyColumns']); + + if ($selfReferential) { + $otherKeys = array_keys($mapping['relationToSourceKeyColumns']); + } + } else { + $keys = array_keys($mapping['relationToSourceKeyColumns']); + + if ($selfReferential) { + $otherKeys = array_keys($mapping['relationToTargetKeyColumns']); + } + } + + if ( ! isset($mapping['isOnDeleteCascade'])) { + $this->_conn->delete( + $this->_class->getQuotedJoinTableName($mapping, $this->_platform), + array_combine($keys, $identifier) + ); + + if ($selfReferential) { + $this->_conn->delete( + $this->_class->getQuotedJoinTableName($mapping, $this->_platform), + array_combine($otherKeys, $identifier) + ); + } + } + } + } + } + + /** + * Deletes a managed entity. + * + * The entity to delete must be managed and have a persistent identifier. + * The deletion happens instantaneously. + * + * Subclasses may override this method to customize the semantics of entity deletion. + * + * @param object $entity The entity to delete. + */ + public function delete($entity) + { + $identifier = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); + $this->deleteJoinTableRecords($identifier); + + $id = array_combine($this->_class->getIdentifierColumnNames(), $identifier); + $this->_conn->delete($this->_class->getQuotedTableName($this->_platform), $id); + } + + /** + * Prepares the changeset of an entity for database insertion (UPDATE). + * + * The changeset is obtained from the currently running UnitOfWork. + * + * During this preparation the array that is passed as the second parameter is filled with + * => pairs, grouped by table name. + * + * Example: + * + * array( + * 'foo_table' => array('column1' => 'value1', 'column2' => 'value2', ...), + * 'bar_table' => array('columnX' => 'valueX', 'columnY' => 'valueY', ...), + * ... + * ) + * + * + * @param object $entity The entity for which to prepare the data. + * @return array The prepared data. + */ + protected function _prepareUpdateData($entity) + { + $result = array(); + $uow = $this->_em->getUnitOfWork(); + + if (($versioned = $this->_class->isVersioned) != false) { + $versionField = $this->_class->versionField; + } + + foreach ($uow->getEntityChangeSet($entity) as $field => $change) { + if ($versioned && $versionField == $field) { + continue; + } + + $oldVal = $change[0]; + $newVal = $change[1]; + + if (isset($this->_class->associationMappings[$field])) { + $assoc = $this->_class->associationMappings[$field]; + + // Only owning side of x-1 associations can have a FK column. + if ( ! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE)) { + continue; + } + + if ($newVal !== null) { + $oid = spl_object_hash($newVal); + + if (isset($this->_queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) { + // The associated entity $newVal is not yet persisted, so we must + // set $newVal = null, in order to insert a null value and schedule an + // extra update on the UnitOfWork. + $uow->scheduleExtraUpdate($entity, array( + $field => array(null, $newVal) + )); + $newVal = null; + } + } + + if ($newVal !== null) { + $newValId = $uow->getEntityIdentifier($newVal); + } + + $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); + $owningTable = $this->getOwningTable($field); + + foreach ($assoc['sourceToTargetKeyColumns'] as $sourceColumn => $targetColumn) { + if ($newVal === null) { + $result[$owningTable][$sourceColumn] = null; + } else if ($targetClass->containsForeignIdentifier) { + $result[$owningTable][$sourceColumn] = $newValId[$targetClass->getFieldForColumn($targetColumn)]; + } else { + $result[$owningTable][$sourceColumn] = $newValId[$targetClass->fieldNames[$targetColumn]]; + } + + $this->_columnTypes[$sourceColumn] = $targetClass->getTypeOfColumn($targetColumn); + } + } else { + $columnName = $this->_class->columnNames[$field]; + $this->_columnTypes[$columnName] = $this->_class->fieldMappings[$field]['type']; + $result[$this->getOwningTable($field)][$columnName] = $newVal; + } + } + + return $result; + } + + /** + * Prepares the data changeset of a managed entity for database insertion (initial INSERT). + * The changeset of the entity is obtained from the currently running UnitOfWork. + * + * The default insert data preparation is the same as for updates. + * + * @param object $entity The entity for which to prepare the data. + * @return array The prepared data for the tables to update. + * @see _prepareUpdateData + */ + protected function _prepareInsertData($entity) + { + return $this->_prepareUpdateData($entity); + } + + /** + * Gets the name of the table that owns the column the given field is mapped to. + * + * The default implementation in BasicEntityPersister always returns the name + * of the table the entity type of this persister is mapped to, since an entity + * is always persisted to a single table with a BasicEntityPersister. + * + * @param string $fieldName The field name. + * @return string The table name. + */ + public function getOwningTable($fieldName) + { + return $this->_class->getTableName(); + } + + /** + * Loads an entity by a list of field criteria. + * + * @param array $criteria The criteria by which to load the entity. + * @param object $entity The entity to load the data into. If not specified, + * a new entity is created. + * @param $assoc The association that connects the entity to load to another entity, if any. + * @param array $hints Hints for entity creation. + * @param int $lockMode + * @param int $limit Limit number of results + * @return object The loaded and managed entity instance or NULL if the entity can not be found. + * @todo Check identity map? loadById method? Try to guess whether $criteria is the id? + */ + public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = 0, $limit = null) + { + $sql = $this->_getSelectEntitiesSQL($criteria, $assoc, $lockMode, $limit); + list($params, $types) = $this->expandParameters($criteria); + $stmt = $this->_conn->executeQuery($sql, $params, $types); + + if ($entity !== null) { + $hints[Query::HINT_REFRESH] = true; + $hints[Query::HINT_REFRESH_ENTITY] = $entity; + } + + $hydrator = $this->_em->newHydrator($this->_selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + $entities = $hydrator->hydrateAll($stmt, $this->_rsm, $hints); + + return $entities ? $entities[0] : null; + } + + /** + * Loads an entity of this persister's mapped class as part of a single-valued + * association from another entity. + * + * @param array $assoc The association to load. + * @param object $sourceEntity The entity that owns the association (not necessarily the "owning side"). + * @param array $identifier The identifier of the entity to load. Must be provided if + * the association to load represents the owning side, otherwise + * the identifier is derived from the $sourceEntity. + * @return object The loaded and managed entity instance or NULL if the entity can not be found. + */ + public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array()) + { + if (($foundEntity = $this->_em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) != false) { + return $foundEntity; + } + + $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); + + if ($assoc['isOwningSide']) { + $isInverseSingleValued = $assoc['inversedBy'] && ! $targetClass->isCollectionValuedAssociation($assoc['inversedBy']); + + // Mark inverse side as fetched in the hints, otherwise the UoW would + // try to load it in a separate query (remember: to-one inverse sides can not be lazy). + $hints = array(); + + if ($isInverseSingleValued) { + $hints['fetched']["r"][$assoc['inversedBy']] = true; + } + + /* cascade read-only status + if ($this->_em->getUnitOfWork()->isReadOnly($sourceEntity)) { + $hints[Query::HINT_READ_ONLY] = true; + } + */ + + $targetEntity = $this->load($identifier, null, $assoc, $hints); + + // Complete bidirectional association, if necessary + if ($targetEntity !== null && $isInverseSingleValued) { + $targetClass->reflFields[$assoc['inversedBy']]->setValue($targetEntity, $sourceEntity); + } + } else { + $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); + $owningAssoc = $targetClass->getAssociationMapping($assoc['mappedBy']); + + // TRICKY: since the association is specular source and target are flipped + foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { + if ( ! isset($sourceClass->fieldNames[$sourceKeyColumn])) { + throw MappingException::joinColumnMustPointToMappedField( + $sourceClass->name, $sourceKeyColumn + ); + } + + // unset the old value and set the new sql aliased value here. By definition + // unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method. + $identifier[$this->_getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] = + $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); + + unset($identifier[$targetKeyColumn]); + } + + $targetEntity = $this->load($identifier, null, $assoc); + + if ($targetEntity !== null) { + $targetClass->setFieldValue($targetEntity, $assoc['mappedBy'], $sourceEntity); + } + } + + return $targetEntity; + } + + /** + * Refreshes a managed entity. + * + * @param array $id The identifier of the entity as an associative array from + * column or field names to values. + * @param object $entity The entity to refresh. + */ + public function refresh(array $id, $entity) + { + $sql = $this->_getSelectEntitiesSQL($id); + list($params, $types) = $this->expandParameters($id); + $stmt = $this->_conn->executeQuery($sql, $params, $types); + + $hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT); + $hydrator->hydrateAll($stmt, $this->_rsm, array(Query::HINT_REFRESH => true)); + } + + /** + * Loads a list of entities by a list of field criteria. + * + * @param array $criteria + * @param array $orderBy + * @param int $limit + * @param int $offset + * @return array + */ + public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null) + { + $entities = array(); + $sql = $this->_getSelectEntitiesSQL($criteria, null, 0, $limit, $offset, $orderBy); + list($params, $types) = $this->expandParameters($criteria); + $stmt = $this->_conn->executeQuery($sql, $params, $types); + + $hydrator = $this->_em->newHydrator(($this->_selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + + return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true)); + } + + /** + * Get (sliced or full) elements of the given collection. + * + * @param array $assoc + * @param object $sourceEntity + * @param int|null $offset + * @param int|null $limit + * @return array + */ + public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit); + + return $this->loadArrayFromStatement($assoc, $stmt); + } + + /** + * Load an array of entities from a given dbal statement. + * + * @param array $assoc + * @param \Doctrine\DBAL\Statement $stmt + * + * @return array + */ + private function loadArrayFromStatement($assoc, $stmt) + { + $hints = array('deferEagerLoads' => true); + + if (isset($assoc['indexBy'])) { + $rsm = clone ($this->_rsm); // this is necessary because the "default rsm" should be changed. + $rsm->addIndexBy('r', $assoc['indexBy']); + } else { + $rsm = $this->_rsm; + } + + $hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT); + + return $hydrator->hydrateAll($stmt, $rsm, $hints); + } + + /** + * Hydrate a collection from a given dbal statement. + * + * @param array $assoc + * @param \Doctrine\DBAL\Statement $stmt + * @param PersistentCollection $coll + * + * @return array + */ + private function loadCollectionFromStatement($assoc, $stmt, $coll) + { + $hints = array('deferEagerLoads' => true, 'collection' => $coll); + + if (isset($assoc['indexBy'])) { + $rsm = clone ($this->_rsm); // this is necessary because the "default rsm" should be changed. + $rsm->addIndexBy('r', $assoc['indexBy']); + } else { + $rsm = $this->_rsm; + } + + $hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT); + + return $hydrator->hydrateAll($stmt, $rsm, $hints); + } + + /** + * Loads a collection of entities of a many-to-many association. + * + * @param ManyToManyMapping $assoc The association mapping of the association being loaded. + * @param object $sourceEntity The entity that owns the collection. + * @param PersistentCollection $coll The collection to fill. + * @param int|null $offset + * @param int|null $limit + * @return array + */ + public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) + { + $stmt = $this->getManyToManyStatement($assoc, $sourceEntity); + + return $this->loadCollectionFromStatement($assoc, $stmt, $coll); + } + + private function getManyToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $criteria = array(); + $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); + + if ($assoc['isOwningSide']) { + $quotedJoinTable = $sourceClass->getQuotedJoinTableName($assoc, $this->_platform); + + foreach ($assoc['relationToSourceKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) { + if ($sourceClass->containsForeignIdentifier) { + $field = $sourceClass->getFieldForColumn($sourceKeyColumn); + $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + + if (isset($sourceClass->associationMappings[$field])) { + $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); + $value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; + } + + $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value; + } else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { + $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); + } else { + throw MappingException::joinColumnMustPointToMappedField( + $sourceClass->name, $sourceKeyColumn + ); + } + } + } else { + $owningAssoc = $this->_em->getClassMetadata($assoc['targetEntity'])->associationMappings[$assoc['mappedBy']]; + $quotedJoinTable = $sourceClass->getQuotedJoinTableName($owningAssoc, $this->_platform); + + // TRICKY: since the association is inverted source and target are flipped + foreach ($owningAssoc['relationToTargetKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) { + if ($sourceClass->containsForeignIdentifier) { + $field = $sourceClass->getFieldForColumn($sourceKeyColumn); + $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + + if (isset($sourceClass->associationMappings[$field])) { + $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); + $value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; + } + + $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value; + } else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { + $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); + } else { + throw MappingException::joinColumnMustPointToMappedField( + $sourceClass->name, $sourceKeyColumn + ); + } + } + } + + $sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset); + list($params, $types) = $this->expandParameters($criteria); + + return $this->_conn->executeQuery($sql, $params, $types); + } + + /** + * Gets the SELECT SQL to select one or more entities by a set of field criteria. + * + * @param array $criteria + * @param AssociationMapping $assoc + * @param string $orderBy + * @param int $lockMode + * @param int $limit + * @param int $offset + * @param array $orderBy + * @return string + * @todo Refactor: _getSelectSQL(...) + */ + protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null) + { + $joinSql = ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) ? $this->_getSelectManyToManyJoinSQL($assoc) : ''; + $conditionSql = $this->_getSelectConditionSQL($criteria, $assoc); + + $orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy; + $orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $this->_getSQLTableAlias($this->_class->name)) : ''; + + $lockSql = ''; + + if ($lockMode == LockMode::PESSIMISTIC_READ) { + $lockSql = ' ' . $this->_platform->getReadLockSql(); + } else if ($lockMode == LockMode::PESSIMISTIC_WRITE) { + $lockSql = ' ' . $this->_platform->getWriteLockSql(); + } + + $alias = $this->_getSQLTableAlias($this->_class->name); + + if ($filterSql = $this->generateFilterConditionSQL($this->_class, $alias)) { + if ($conditionSql) { + $conditionSql .= ' AND '; + } + + $conditionSql .= $filterSql; + } + + return $this->_platform->modifyLimitQuery('SELECT ' . $this->_getSelectColumnListSQL() + . $this->_platform->appendLockHint(' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' + . $alias, $lockMode) + . $this->_selectJoinSql . $joinSql + . ($conditionSql ? ' WHERE ' . $conditionSql : '') + . $orderBySql, $limit, $offset) + . $lockSql; + } + + /** + * Gets the ORDER BY SQL snippet for ordered collections. + * + * @param array $orderBy + * @param string $baseTableAlias + * @return string + */ + protected final function _getOrderBySQL(array $orderBy, $baseTableAlias) + { + $orderBySql = ''; + + foreach ($orderBy as $fieldName => $orientation) { + if ( ! isset($this->_class->fieldMappings[$fieldName])) { + throw ORMException::unrecognizedField($fieldName); + } + + $orientation = strtoupper(trim($orientation)); + if ($orientation != 'ASC' && $orientation != 'DESC') { + throw ORMException::invalidOrientation($this->_class->name, $fieldName); + } + + $tableAlias = isset($this->_class->fieldMappings[$fieldName]['inherited']) ? + $this->_getSQLTableAlias($this->_class->fieldMappings[$fieldName]['inherited']) + : $baseTableAlias; + + $columnName = $this->_class->getQuotedColumnName($fieldName, $this->_platform); + + $orderBySql .= $orderBySql ? ', ' : ' ORDER BY '; + $orderBySql .= $tableAlias . '.' . $columnName . ' ' . $orientation; + } + + return $orderBySql; + } + + /** + * Gets the SQL fragment with the list of columns to select when querying for + * an entity in this persister. + * + * Subclasses should override this method to alter or change the select column + * list SQL fragment. Note that in the implementation of BasicEntityPersister + * the resulting SQL fragment is generated only once and cached in {@link _selectColumnListSql}. + * Subclasses may or may not do the same. + * + * @return string The SQL fragment. + * @todo Rename: _getSelectColumnsSQL() + */ + protected function _getSelectColumnListSQL() + { + if ($this->_selectColumnListSql !== null) { + return $this->_selectColumnListSql; + } + + $columnList = ''; + $this->_rsm = new Query\ResultSetMapping(); + $this->_rsm->addEntityResult($this->_class->name, 'r'); // r for root + + // Add regular columns to select list + foreach ($this->_class->fieldNames as $field) { + if ($columnList) $columnList .= ', '; + + $columnList .= $this->_getSelectColumnSQL($field, $this->_class); + } + + $this->_selectJoinSql = ''; + $eagerAliasCounter = 0; + + foreach ($this->_class->associationMappings as $assocField => $assoc) { + $assocColumnSQL = $this->_getSelectColumnAssociationSQL($assocField, $assoc, $this->_class); + + if ($assocColumnSQL) { + if ($columnList) $columnList .= ', '; + + $columnList .= $assocColumnSQL; + } + + if ($assoc['type'] & ClassMetadata::TO_ONE && ($assoc['fetch'] == ClassMetadata::FETCH_EAGER || !$assoc['isOwningSide'])) { + $eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']); + + if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) { + continue; // now this is why you shouldn't use inheritance + } + + $assocAlias = 'e' . ($eagerAliasCounter++); + $this->_rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField); + + foreach ($eagerEntity->fieldNames AS $field) { + if ($columnList) $columnList .= ', '; + + $columnList .= $this->_getSelectColumnSQL($field, $eagerEntity, $assocAlias); + } + + foreach ($eagerEntity->associationMappings as $assoc2Field => $assoc2) { + $assoc2ColumnSQL = $this->_getSelectColumnAssociationSQL($assoc2Field, $assoc2, $eagerEntity, $assocAlias); + + if ($assoc2ColumnSQL) { + if ($columnList) $columnList .= ', '; + $columnList .= $assoc2ColumnSQL; + } + } + $first = true; + + if ($assoc['isOwningSide']) { + $this->_selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($assoc['joinColumns']); + $this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON '; + + $tableAlias = $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias); + foreach ($assoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) { + if ( ! $first) { + $this->_selectJoinSql .= ' AND '; + } + $this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.' . $sourceCol . ' = ' + . $tableAlias . '.' . $targetCol; + $first = false; + } + + // Add filter SQL + if ($filterSql = $this->generateFilterConditionSQL($eagerEntity, $tableAlias)) { + $this->_selectJoinSql .= ' AND ' . $filterSql; + } + } else { + $eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']); + $owningAssoc = $eagerEntity->getAssociationMapping($assoc['mappedBy']); + + $this->_selectJoinSql .= ' LEFT JOIN'; + $this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' ' + . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) . ' ON '; + + foreach ($owningAssoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) { + if ( ! $first) { + $this->_selectJoinSql .= ' AND '; + } + + $this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = ' + . $this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol; + $first = false; + } + } + } + } + + $this->_selectColumnListSql = $columnList; + + return $this->_selectColumnListSql; + } + + /** + * Gets the SQL join fragment used when selecting entities from an association. + * + * @param string $field + * @param array $assoc + * @param ClassMetadata $class + * @param string $alias + * + * @return string + */ + protected function _getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r') + { + $columnList = ''; + + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { + if ($columnList) $columnList .= ', '; + + $resultColumnName = $this->getSQLColumnAlias($srcColumn); + $columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) + . '.' . $srcColumn . ' AS ' . $resultColumnName; + $this->_rsm->addMetaResult($alias, $resultColumnName, $srcColumn, isset($assoc['id']) && $assoc['id'] === true); + } + } + + return $columnList; + } + + /** + * Gets the SQL join fragment used when selecting entities from a + * many-to-many association. + * + * @param ManyToManyMapping $manyToMany + * @return string + */ + protected function _getSelectManyToManyJoinSQL(array $manyToMany) + { + if ($manyToMany['isOwningSide']) { + $owningAssoc = $manyToMany; + $joinClauses = $manyToMany['relationToTargetKeyColumns']; + } else { + $owningAssoc = $this->_em->getClassMetadata($manyToMany['targetEntity'])->associationMappings[$manyToMany['mappedBy']]; + $joinClauses = $owningAssoc['relationToSourceKeyColumns']; + } + + $joinTableName = $this->_class->getQuotedJoinTableName($owningAssoc, $this->_platform); + $joinSql = ''; + + foreach ($joinClauses as $joinTableColumn => $sourceColumn) { + if ($joinSql != '') $joinSql .= ' AND '; + + if ($this->_class->containsForeignIdentifier && ! isset($this->_class->fieldNames[$sourceColumn])) { + $quotedColumn = $sourceColumn; // join columns cannot be quoted + } else { + $quotedColumn = $this->_class->getQuotedColumnName($this->_class->fieldNames[$sourceColumn], $this->_platform); + } + + $joinSql .= $this->_getSQLTableAlias($this->_class->name) . '.' . $quotedColumn . ' = ' + . $joinTableName . '.' . $joinTableColumn; + } + + return ' INNER JOIN ' . $joinTableName . ' ON ' . $joinSql; + } + + /** + * Gets the INSERT SQL used by the persister to persist a new entity. + * + * @return string + */ + protected function _getInsertSQL() + { + if ($this->_insertSql === null) { + $insertSql = ''; + $columns = $this->_getInsertColumnList(); + + if (empty($columns)) { + $insertSql = $this->_platform->getEmptyIdentityInsertSQL( + $this->_class->getQuotedTableName($this->_platform), + $this->_class->getQuotedColumnName($this->_class->identifier[0], $this->_platform) + ); + } else { + $columns = array_unique($columns); + + $values = array(); + foreach ($columns AS $column) { + $placeholder = '?'; + + if (isset($this->_columnTypes[$column]) && + isset($this->_class->fieldNames[$column]) && + isset($this->_class->fieldMappings[$this->_class->fieldNames[$column]]['requireSQLConversion'])) { + $type = Type::getType($this->_columnTypes[$column]); + $placeholder = $type->convertToDatabaseValueSQL('?', $this->_platform); + } + + $values[] = $placeholder; + } + + $insertSql = 'INSERT INTO ' . $this->_class->getQuotedTableName($this->_platform) + . ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $values) . ')'; + } + + $this->_insertSql = $insertSql; + } + + return $this->_insertSql; + } + + /** + * Gets the list of columns to put in the INSERT SQL statement. + * + * Subclasses should override this method to alter or change the list of + * columns placed in the INSERT statements used by the persister. + * + * @return array The list of columns. + */ + protected function _getInsertColumnList() + { + $columns = array(); + + foreach ($this->_class->reflFields as $name => $field) { + if ($this->_class->isVersioned && $this->_class->versionField == $name) { + continue; + } + + if (isset($this->_class->associationMappings[$name])) { + $assoc = $this->_class->associationMappings[$name]; + + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) { + $columns[] = $sourceCol; + } + } + } else if ($this->_class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || $this->_class->identifier[0] != $name) { + $columns[] = $this->_class->getQuotedColumnName($name, $this->_platform); + $this->_columnTypes[$name] = $this->_class->fieldMappings[$name]['type']; + } + } + + return $columns; + } + + /** + * Gets the SQL snippet of a qualified column name for the given field name. + * + * @param string $field The field name. + * @param ClassMetadata $class The class that declares this field. The table this class is + * mapped to must own the column for the given field. + * @param string $alias + */ + protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') + { + $sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) + . '.' . $class->getQuotedColumnName($field, $this->_platform); + $columnAlias = $this->getSQLColumnAlias($class->columnNames[$field]); + + $this->_rsm->addFieldResult($alias, $columnAlias, $field); + + if (isset($class->fieldMappings[$field]['requireSQLConversion'])) { + $type = Type::getType($class->getTypeOfField($field)); + $sql = $type->convertToPHPValueSQL($sql, $this->_platform); + } + + return $sql . ' AS ' . $columnAlias; + } + + /** + * Gets the SQL table alias for the given class name. + * + * @param string $className + * @return string The SQL table alias. + * @todo Reconsider. Binding table aliases to class names is not such a good idea. + */ + protected function _getSQLTableAlias($className, $assocName = '') + { + if ($assocName) { + $className .= '#' . $assocName; + } + + if (isset($this->_sqlTableAliases[$className])) { + return $this->_sqlTableAliases[$className]; + } + + $tableAlias = 't' . $this->_sqlAliasCounter++; + + $this->_sqlTableAliases[$className] = $tableAlias; + + return $tableAlias; + } + + /** + * Lock all rows of this entity matching the given criteria with the specified pessimistic lock mode + * + * @param array $criteria + * @param int $lockMode + * @return void + */ + public function lock(array $criteria, $lockMode) + { + $conditionSql = $this->_getSelectConditionSQL($criteria); + + if ($lockMode == LockMode::PESSIMISTIC_READ) { + $lockSql = $this->_platform->getReadLockSql(); + } else if ($lockMode == LockMode::PESSIMISTIC_WRITE) { + $lockSql = $this->_platform->getWriteLockSql(); + } + + $sql = 'SELECT 1 ' + . $this->_platform->appendLockHint($this->getLockTablesSql(), $lockMode) + . ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ' . $lockSql; + + list($params, $types) = $this->expandParameters($criteria); + + $stmt = $this->_conn->executeQuery($sql, $params, $types); + } + + /** + * Get the FROM and optionally JOIN conditions to lock the entity managed by this persister. + * + * @return string + */ + protected function getLockTablesSql() + { + return 'FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' + . $this->_getSQLTableAlias($this->_class->name); + } + + /** + * Gets the conditional SQL fragment used in the WHERE clause when selecting + * entities in this persister. + * + * Subclasses are supposed to override this method if they intend to change + * or alter the criteria by which entities are selected. + * + * @param array $criteria + * @param AssociationMapping $assoc + * @return string + */ + protected function _getSelectConditionSQL(array $criteria, $assoc = null) + { + $conditionSql = ''; + + foreach ($criteria as $field => $value) { + $conditionSql .= $conditionSql ? ' AND ' : ''; + + $placeholder = '?'; + + if (isset($this->_class->columnNames[$field])) { + $className = (isset($this->_class->fieldMappings[$field]['inherited'])) + ? $this->_class->fieldMappings[$field]['inherited'] + : $this->_class->name; + + $conditionSql .= $this->_getSQLTableAlias($className) . '.' . $this->_class->getQuotedColumnName($field, $this->_platform); + + if (isset($this->_class->fieldMappings[$field]['requireSQLConversion'])) { + $type = Type::getType($this->_class->getTypeOfField($field)); + $placeholder = $type->convertToDatabaseValueSQL($placeholder, $this->_platform); + } + } else if (isset($this->_class->associationMappings[$field])) { + if ( ! $this->_class->associationMappings[$field]['isOwningSide']) { + throw ORMException::invalidFindByInverseAssociation($this->_class->name, $field); + } + + $className = (isset($this->_class->associationMappings[$field]['inherited'])) + ? $this->_class->associationMappings[$field]['inherited'] + : $this->_class->name; + + $conditionSql .= $this->_getSQLTableAlias($className) . '.' . $this->_class->associationMappings[$field]['joinColumns'][0]['name']; + } else if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) { + // very careless developers could potentially open up this normally hidden api for userland attacks, + // therefore checking for spaces and function calls which are not allowed. + + // found a join column condition, not really a "field" + $conditionSql .= $field; + } else { + throw ORMException::unrecognizedField($field); + } + + $conditionSql .= (is_array($value)) ? ' IN (?)' : (($value === null) ? ' IS NULL' : ' = ' . $placeholder); + } + return $conditionSql; + } + + /** + * Return an array with (sliced or full list) of elements in the specified collection. + * + * @param array $assoc + * @param object $sourceEntity + * @param int $offset + * @param int $limit + * @return array + */ + public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit); + + return $this->loadArrayFromStatement($assoc, $stmt); + } + + /** + * Loads a collection of entities in a one-to-many association. + * + * @param array $assoc + * @param object $sourceEntity + * @param PersistentCollection $coll The collection to load/fill. + * @param int|null $offset + * @param int|null $limit + */ + public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) + { + $stmt = $this->getOneToManyStatement($assoc, $sourceEntity); + + return $this->loadCollectionFromStatement($assoc, $stmt, $coll); + } + + /** + * Build criteria and execute SQL statement to fetch the one to many entities from. + * + * @param array $assoc + * @param object $sourceEntity + * @param int|null $offset + * @param int|null $limit + * @return \Doctrine\DBAL\Statement + */ + private function getOneToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $criteria = array(); + $owningAssoc = $this->_class->associationMappings[$assoc['mappedBy']]; + $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); + + $tableAlias = $this->_getSQLTableAlias(isset($owningAssoc['inherited']) ? $owningAssoc['inherited'] : $this->_class->name); + + foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { + if ($sourceClass->containsForeignIdentifier) { + $field = $sourceClass->getFieldForColumn($sourceKeyColumn); + $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + + if (isset($sourceClass->associationMappings[$field])) { + $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); + $value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; + } + + $criteria[$tableAlias . "." . $targetKeyColumn] = $value; + } else { + $criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); + } + } + + $sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset); + list($params, $types) = $this->expandParameters($criteria); + + return $this->_conn->executeQuery($sql, $params, $types); + } + + /** + * Expand the parameters from the given criteria and use the correct binding types if found. + * + * @param array $criteria + * @return array + */ + private function expandParameters($criteria) + { + $params = $types = array(); + + foreach ($criteria AS $field => $value) { + if ($value === null) { + continue; // skip null values. + } + + $types[] = $this->getType($field, $value); + $params[] = $this->getValue($value); + } + + return array($params, $types); + } + + /** + * Infer field type to be used by parameter type casting. + * + * @param string $field + * @param mixed $value + * @return integer + */ + private function getType($field, $value) + { + switch (true) { + case (isset($this->_class->fieldMappings[$field])): + $type = $this->_class->fieldMappings[$field]['type']; + break; + + case (isset($this->_class->associationMappings[$field])): + $assoc = $this->_class->associationMappings[$field]; + + if (count($assoc['sourceToTargetKeyColumns']) > 1) { + throw Query\QueryException::associationPathCompositeKeyNotSupported(); + } + + $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); + $targetColumn = $assoc['joinColumns'][0]['referencedColumnName']; + $type = null; + + if (isset($targetClass->fieldNames[$targetColumn])) { + $type = $targetClass->fieldMappings[$targetClass->fieldNames[$targetColumn]]['type']; + } + + break; + + default: + $type = null; + } + if (is_array($value)) { + $type = Type::getType( $type )->getBindingType(); + $type += Connection::ARRAY_PARAM_OFFSET; + } + + return $type; + } + + /** + * Retrieve parameter value + * + * @param mixed $value + * @return mixed + */ + private function getValue($value) + { + if (is_array($value)) { + $newValue = array(); + + foreach ($value as $itemValue) { + $newValue[] = $this->getIndividualValue($itemValue); + } + + return $newValue; + } + + return $this->getIndividualValue($value); + } + + /** + * Retrieve an invidiual parameter value + * + * @param mixed $value + * @return mixed + */ + private function getIndividualValue($value) + { + if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) { + if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) { + $idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value); + } else { + $class = $this->_em->getClassMetadata(get_class($value)); + $idValues = $class->getIdentifierValues($value); + } + + $value = $idValues[key($idValues)]; + } + + return $value; + } + + /** + * Checks whether the given managed entity exists in the database. + * + * @param object $entity + * @return boolean TRUE if the entity exists in the database, FALSE otherwise. + */ + public function exists($entity, array $extraConditions = array()) + { + $criteria = $this->_class->getIdentifierValues($entity); + + if ( ! $criteria) { + return false; + } + + if ($extraConditions) { + $criteria = array_merge($criteria, $extraConditions); + } + + $alias = $this->_getSQLTableAlias($this->_class->name); + + $sql = 'SELECT 1 ' + . $this->getLockTablesSql() + . ' WHERE ' . $this->_getSelectConditionSQL($criteria); + + if ($filterSql = $this->generateFilterConditionSQL($this->_class, $alias)) { + $sql .= ' AND ' . $filterSql; + } + + list($params, $types) = $this->expandParameters($criteria); + + return (bool) $this->_conn->fetchColumn($sql, $params); + } + + /** + * Generates the appropriate join SQL for the given join column. + * + * @param array $joinColumns The join columns definition of an association. + * @return string LEFT JOIN if one of the columns is nullable, INNER JOIN otherwise. + */ + protected function getJoinSQLForJoinColumns($joinColumns) + { + // if one of the join columns is nullable, return left join + foreach ($joinColumns as $joinColumn) { + if (!isset($joinColumn['nullable']) || $joinColumn['nullable']) { + return 'LEFT JOIN'; + } + } + + return 'INNER JOIN'; + } + + /** + * Gets an SQL column alias for a column name. + * + * @param string $columnName + * @return string + */ + public function getSQLColumnAlias($columnName) + { + // Trim the column alias to the maximum identifier length of the platform. + // If the alias is to long, characters are cut off from the beginning. + return $this->_platform->getSQLResultCasing( + substr($columnName . $this->_sqlAliasCounter++, -$this->_platform->getMaxIdentifierLength()) + ); + } + + /** + * Generates the filter SQL for a given entity and table alias. + * + * @param ClassMetadata $targetEntity Metadata of the target entity. + * @param string $targetTableAlias The table alias of the joined/selected table. + * + * @return string The SQL query part to add to a query. + */ + protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + { + $filterClauses = array(); + + foreach ($this->_em->getFilters()->getEnabledFilters() as $filter) { + if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) { + $filterClauses[] = '(' . $filterExpr . ')'; + } + } + + $sql = implode(' AND ', $filterClauses); + return $sql ? "(" . $sql . ")" : ""; // Wrap again to avoid "X or Y and FilterConditionSQL" + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ElementCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ElementCollectionPersister.php new file mode 100644 index 0000000..688edeb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ElementCollectionPersister.php @@ -0,0 +1,33 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +/** + * Persister for collections of basic elements / value types. + * + * @author robo + * @todo Implementation once support for collections of basic elements (i.e. strings) is added. + */ +abstract class ElementCollectionPersister extends AbstractCollectionPersister +{ + //put your code here +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php new file mode 100644 index 0000000..350ced9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -0,0 +1,487 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\ORMException, + Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\DBAL\LockMode, + Doctrine\DBAL\Types\Type, + Doctrine\ORM\Query\ResultSetMapping; + +/** + * The joined subclass persister maps a single entity instance to several tables in the + * database as it is defined by the Class Table Inheritance strategy. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Alexander + * @since 2.0 + * @see http://martinfowler.com/eaaCatalog/classTableInheritance.html + */ +class JoinedSubclassPersister extends AbstractEntityInheritancePersister +{ + /** + * Map that maps column names to the table names that own them. + * This is mainly a temporary cache, used during a single request. + * + * @var array + */ + private $_owningTableMap = array(); + + /** + * Map of table to quoted table names. + * + * @var array + */ + private $_quotedTableMap = array(); + + /** + * {@inheritdoc} + */ + protected function _getDiscriminatorColumnTableName() + { + $class = ($this->_class->name !== $this->_class->rootEntityName) + ? $this->_em->getClassMetadata($this->_class->rootEntityName) + : $this->_class; + + return $class->getTableName(); + } + + /** + * This function finds the ClassMetadata instance in an inheritance hierarchy + * that is responsible for enabling versioning. + * + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + private function _getVersionedClassMetadata() + { + if (isset($this->_class->fieldMappings[$this->_class->versionField]['inherited'])) { + $definingClassName = $this->_class->fieldMappings[$this->_class->versionField]['inherited']; + + return $this->_em->getClassMetadata($definingClassName); + } + + return $this->_class; + } + + /** + * Gets the name of the table that owns the column the given field is mapped to. + * + * @param string $fieldName + * @return string + * @override + */ + public function getOwningTable($fieldName) + { + if (isset($this->_owningTableMap[$fieldName])) { + return $this->_owningTableMap[$fieldName]; + } + + if (isset($this->_class->associationMappings[$fieldName]['inherited'])) { + $cm = $this->_em->getClassMetadata($this->_class->associationMappings[$fieldName]['inherited']); + } else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) { + $cm = $this->_em->getClassMetadata($this->_class->fieldMappings[$fieldName]['inherited']); + } else { + $cm = $this->_class; + } + + $tableName = $cm->getTableName(); + + $this->_owningTableMap[$fieldName] = $tableName; + $this->_quotedTableMap[$tableName] = $cm->getQuotedTableName($this->_platform); + + return $tableName; + } + + /** + * {@inheritdoc} + */ + public function executeInserts() + { + if ( ! $this->_queuedInserts) { + return; + } + + $postInsertIds = array(); + $idGen = $this->_class->idGenerator; + $isPostInsertId = $idGen->isPostInsertGenerator(); + + // Prepare statement for the root table + $rootClass = ($this->_class->name !== $this->_class->rootEntityName) ? $this->_em->getClassMetadata($this->_class->rootEntityName) : $this->_class; + $rootPersister = $this->_em->getUnitOfWork()->getEntityPersister($rootClass->name); + $rootTableName = $rootClass->getTableName(); + $rootTableStmt = $this->_conn->prepare($rootPersister->_getInsertSQL()); + + // Prepare statements for sub tables. + $subTableStmts = array(); + + if ($rootClass !== $this->_class) { + $subTableStmts[$this->_class->getTableName()] = $this->_conn->prepare($this->_getInsertSQL()); + } + + foreach ($this->_class->parentClasses as $parentClassName) { + $parentClass = $this->_em->getClassMetadata($parentClassName); + $parentTableName = $parentClass->getTableName(); + + if ($parentClass !== $rootClass) { + $parentPersister = $this->_em->getUnitOfWork()->getEntityPersister($parentClassName); + $subTableStmts[$parentTableName] = $this->_conn->prepare($parentPersister->_getInsertSQL()); + } + } + + // Execute all inserts. For each entity: + // 1) Insert on root table + // 2) Insert on sub tables + foreach ($this->_queuedInserts as $entity) { + $insertData = $this->_prepareInsertData($entity); + + // Execute insert on root table + $paramIndex = 1; + + foreach ($insertData[$rootTableName] as $columnName => $value) { + $rootTableStmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]); + } + + $rootTableStmt->execute(); + + if ($isPostInsertId) { + $id = $idGen->generate($this->_em, $entity); + $postInsertIds[$id] = $entity; + } else { + $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); + } + + // Execute inserts on subtables. + // The order doesn't matter because all child tables link to the root table via FK. + foreach ($subTableStmts as $tableName => $stmt) { + $data = isset($insertData[$tableName]) ? $insertData[$tableName] : array(); + $paramIndex = 1; + + foreach ((array) $id as $idName => $idVal) { + $type = isset($this->_columnTypes[$idName]) ? $this->_columnTypes[$idName] : Type::STRING; + + $stmt->bindValue($paramIndex++, $idVal, $type); + } + + foreach ($data as $columnName => $value) { + $stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]); + } + + $stmt->execute(); + } + } + + $rootTableStmt->closeCursor(); + + foreach ($subTableStmts as $stmt) { + $stmt->closeCursor(); + } + + if ($this->_class->isVersioned) { + $this->assignDefaultVersionValue($entity, $id); + } + + $this->_queuedInserts = array(); + + return $postInsertIds; + } + + /** + * {@inheritdoc} + */ + public function update($entity) + { + $updateData = $this->_prepareUpdateData($entity); + + if (($isVersioned = $this->_class->isVersioned) != false) { + $versionedClass = $this->_getVersionedClassMetadata(); + $versionedTable = $versionedClass->getTableName(); + } + + if ($updateData) { + foreach ($updateData as $tableName => $data) { + $this->_updateTable( + $entity, $this->_quotedTableMap[$tableName], $data, $isVersioned && $versionedTable == $tableName + ); + } + + // Make sure the table with the version column is updated even if no columns on that + // table were affected. + if ($isVersioned && ! isset($updateData[$versionedTable])) { + $this->_updateTable($entity, $versionedClass->getQuotedTableName($this->_platform), array(), true); + + $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); + $this->assignDefaultVersionValue($entity, $id); + } + } + } + + /** + * {@inheritdoc} + */ + public function delete($entity) + { + $identifier = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); + $this->deleteJoinTableRecords($identifier); + + $id = array_combine($this->_class->getIdentifierColumnNames(), $identifier); + + // If the database platform supports FKs, just + // delete the row from the root table. Cascades do the rest. + if ($this->_platform->supportsForeignKeyConstraints()) { + $this->_conn->delete( + $this->_em->getClassMetadata($this->_class->rootEntityName)->getQuotedTableName($this->_platform), $id + ); + } else { + // Delete from all tables individually, starting from this class' table up to the root table. + $this->_conn->delete($this->_class->getQuotedTableName($this->_platform), $id); + + foreach ($this->_class->parentClasses as $parentClass) { + $this->_conn->delete( + $this->_em->getClassMetadata($parentClass)->getQuotedTableName($this->_platform), $id + ); + } + } + } + + /** + * {@inheritdoc} + */ + protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null) + { + $idColumns = $this->_class->getIdentifierColumnNames(); + $baseTableAlias = $this->_getSQLTableAlias($this->_class->name); + + // Create the column list fragment only once + if ($this->_selectColumnListSql === null) { + + $this->_rsm = new ResultSetMapping(); + $this->_rsm->addEntityResult($this->_class->name, 'r'); + + // Add regular columns + $columnList = ''; + + foreach ($this->_class->fieldMappings as $fieldName => $mapping) { + if ($columnList != '') $columnList .= ', '; + + $columnList .= $this->_getSelectColumnSQL( + $fieldName, + isset($mapping['inherited']) ? $this->_em->getClassMetadata($mapping['inherited']) : $this->_class + ); + } + + // Add foreign key columns + foreach ($this->_class->associationMappings as $assoc2) { + if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE) { + $tableAlias = isset($assoc2['inherited']) ? $this->_getSQLTableAlias($assoc2['inherited']) : $baseTableAlias; + + foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) { + if ($columnList != '') $columnList .= ', '; + + $columnList .= $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, + isset($assoc2['inherited']) ? $assoc2['inherited'] : $this->_class->name + ); + } + } + } + + // Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#_processSQLResult). + $discrColumn = $this->_class->discriminatorColumn['name']; + $tableAlias = ($this->_class->rootEntityName == $this->_class->name) ? $baseTableAlias : $this->_getSQLTableAlias($this->_class->rootEntityName); + $columnList .= ', ' . $tableAlias . '.' . $discrColumn; + + $resultColumnName = $this->_platform->getSQLResultCasing($discrColumn); + + $this->_rsm->setDiscriminatorColumn('r', $resultColumnName); + $this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn); + } + + // INNER JOIN parent tables + $joinSql = ''; + + foreach ($this->_class->parentClasses as $parentClassName) { + $parentClass = $this->_em->getClassMetadata($parentClassName); + $tableAlias = $this->_getSQLTableAlias($parentClassName); + $joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; + $first = true; + + foreach ($idColumns as $idColumn) { + if ($first) $first = false; else $joinSql .= ' AND '; + + $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + } + + // OUTER JOIN sub tables + foreach ($this->_class->subClasses as $subClassName) { + $subClass = $this->_em->getClassMetadata($subClassName); + $tableAlias = $this->_getSQLTableAlias($subClassName); + + if ($this->_selectColumnListSql === null) { + // Add subclass columns + foreach ($subClass->fieldMappings as $fieldName => $mapping) { + if (isset($mapping['inherited'])) continue; + + $columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass); + } + + // Add join columns (foreign keys) + foreach ($subClass->associationMappings as $assoc2) { + if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE && ! isset($assoc2['inherited'])) { + foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) { + if ($columnList != '') $columnList .= ', '; + + $columnList .= $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, + isset($assoc2['inherited']) ? $assoc2['inherited'] : $subClass->name + ); + } + } + } + } + + // Add LEFT JOIN + $joinSql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; + $first = true; + + foreach ($idColumns as $idColumn) { + if ($first) $first = false; else $joinSql .= ' AND '; + + $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + } + + $joinSql .= ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) ? $this->_getSelectManyToManyJoinSQL($assoc) : ''; + + $conditionSql = $this->_getSelectConditionSQL($criteria, $assoc); + + // If the current class in the root entity, add the filters + if ($filterSql = $this->generateFilterConditionSQL($this->_em->getClassMetadata($this->_class->rootEntityName), $this->_getSQLTableAlias($this->_class->rootEntityName))) { + if ($conditionSql) { + $conditionSql .= ' AND '; + } + + $conditionSql .= $filterSql; + } + + $orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy; + $orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $baseTableAlias) : ''; + + if ($this->_selectColumnListSql === null) { + $this->_selectColumnListSql = $columnList; + } + + $lockSql = ''; + + if ($lockMode == LockMode::PESSIMISTIC_READ) { + $lockSql = ' ' . $this->_platform->getReadLockSql(); + } else if ($lockMode == LockMode::PESSIMISTIC_WRITE) { + $lockSql = ' ' . $this->_platform->getWriteLockSql(); + } + + return $this->_platform->modifyLimitQuery('SELECT ' . $this->_selectColumnListSql + . ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $baseTableAlias + . $joinSql + . ($conditionSql != '' ? ' WHERE ' . $conditionSql : '') . $orderBySql, $limit, $offset) + . $lockSql; + } + + /** + * Get the FROM and optionally JOIN conditions to lock the entity managed by this persister. + * + * @return string + */ + public function getLockTablesSql() + { + $idColumns = $this->_class->getIdentifierColumnNames(); + $baseTableAlias = $this->_getSQLTableAlias($this->_class->name); + + // INNER JOIN parent tables + $joinSql = ''; + + foreach ($this->_class->parentClasses as $parentClassName) { + $parentClass = $this->_em->getClassMetadata($parentClassName); + $tableAlias = $this->_getSQLTableAlias($parentClassName); + $joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; + $first = true; + + foreach ($idColumns as $idColumn) { + if ($first) $first = false; else $joinSql .= ' AND '; + + $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + } + + return 'FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $baseTableAlias . $joinSql; + } + + /* Ensure this method is never called. This persister overrides _getSelectEntitiesSQL directly. */ + protected function _getSelectColumnListSQL() + { + throw new \BadMethodCallException("Illegal invocation of ".__METHOD__."."); + } + + /** {@inheritdoc} */ + protected function _getInsertColumnList() + { + // Identifier columns must always come first in the column list of subclasses. + $columns = $this->_class->parentClasses ? $this->_class->getIdentifierColumnNames() : array(); + + foreach ($this->_class->reflFields as $name => $field) { + if (isset($this->_class->fieldMappings[$name]['inherited']) && ! isset($this->_class->fieldMappings[$name]['id']) + || isset($this->_class->associationMappings[$name]['inherited']) + || ($this->_class->isVersioned && $this->_class->versionField == $name)) { + continue; + } + + if (isset($this->_class->associationMappings[$name])) { + $assoc = $this->_class->associationMappings[$name]; + if ($assoc['type'] & ClassMetadata::TO_ONE && $assoc['isOwningSide']) { + foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) { + $columns[] = $sourceCol; + } + } + } else if ($this->_class->name != $this->_class->rootEntityName || + ! $this->_class->isIdGeneratorIdentity() || $this->_class->identifier[0] != $name) { + $columns[] = $this->_class->getQuotedColumnName($name, $this->_platform); + } + } + + // Add discriminator column if it is the topmost class. + if ($this->_class->name == $this->_class->rootEntityName) { + $columns[] = $this->_class->discriminatorColumn['name']; + } + + return $columns; + } + + /** + * {@inheritdoc} + */ + protected function assignDefaultVersionValue($entity, $id) + { + $value = $this->fetchVersionValue($this->_getVersionedClassMetadata(), $id); + $this->_class->setFieldValue($entity, $this->_class->versionField, $value); + } + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php new file mode 100644 index 0000000..daef037 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -0,0 +1,425 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\PersistentCollection, + Doctrine\ORM\UnitOfWork; + +/** + * Persister for many-to-many collections. + * + * @author Roman Borschel + * @author Guilherme Blanco + * @author Alexander + * @since 2.0 + */ +class ManyToManyPersister extends AbstractCollectionPersister +{ + /** + * {@inheritdoc} + * + * @override + */ + protected function _getDeleteRowSQL(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + $class = $this->_em->getClassMetadata(get_class($coll->getOwner())); + + return 'DELETE FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform()) + . ' WHERE ' . implode(' = ? AND ', $mapping['joinTableColumns']) . ' = ?'; + } + + /** + * {@inheritdoc} + * + * @override + * @internal Order of the parameters must be the same as the order of the columns in + * _getDeleteRowSql. + */ + protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element) + { + return $this->_collectJoinTableColumnParameters($coll, $element); + } + + /** + * {@inheritdoc} + * + * @override + */ + protected function _getUpdateRowSQL(PersistentCollection $coll) + {} + + /** + * {@inheritdoc} + * + * @override + * @internal Order of the parameters must be the same as the order of the columns in + * _getInsertRowSql. + */ + protected function _getInsertRowSQL(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + $columns = $mapping['joinTableColumns']; + $class = $this->_em->getClassMetadata(get_class($coll->getOwner())); + + $joinTable = $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform()); + + return 'INSERT INTO ' . $joinTable . ' (' . implode(', ', $columns) . ')' + . ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')'; + } + + /** + * {@inheritdoc} + * + * @override + * @internal Order of the parameters must be the same as the order of the columns in + * _getInsertRowSql. + */ + protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element) + { + return $this->_collectJoinTableColumnParameters($coll, $element); + } + + /** + * Collects the parameters for inserting/deleting on the join table in the order + * of the join table columns as specified in ManyToManyMapping#joinTableColumns. + * + * @param $coll + * @param $element + * @return array + */ + private function _collectJoinTableColumnParameters(PersistentCollection $coll, $element) + { + $params = array(); + $mapping = $coll->getMapping(); + $isComposite = count($mapping['joinTableColumns']) > 2; + + $identifier1 = $this->_uow->getEntityIdentifier($coll->getOwner()); + $identifier2 = $this->_uow->getEntityIdentifier($element); + + if ($isComposite) { + $class1 = $this->_em->getClassMetadata(get_class($coll->getOwner())); + $class2 = $coll->getTypeClass(); + } + + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + $isRelationToSource = isset($mapping['relationToSourceKeyColumns'][$joinTableColumn]); + + if ( ! $isComposite) { + $params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2); + + continue; + } + + if ($isRelationToSource) { + $params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]; + + continue; + } + + $params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]; + } + + return $params; + } + + /** + * {@inheritdoc} + * + * @override + */ + protected function _getDeleteSQL(PersistentCollection $coll) + { + $class = $this->_em->getClassMetadata(get_class($coll->getOwner())); + $mapping = $coll->getMapping(); + + return 'DELETE FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform()) + . ' WHERE ' . implode(' = ? AND ', array_keys($mapping['relationToSourceKeyColumns'])) . ' = ?'; + } + + /** + * {@inheritdoc} + * + * @override + * @internal Order of the parameters must be the same as the order of the columns in + * _getDeleteSql. + */ + protected function _getDeleteSQLParameters(PersistentCollection $coll) + { + $identifier = $this->_uow->getEntityIdentifier($coll->getOwner()); + $mapping = $coll->getMapping(); + $params = array(); + + // Optimization for single column identifier + if (count($mapping['relationToSourceKeyColumns']) === 1) { + $params[] = array_pop($identifier); + + return $params; + } + + // Composite identifier + $sourceClass = $this->_em->getClassMetadata(get_class($mapping->getOwner())); + + foreach ($mapping['relationToSourceKeyColumns'] as $relColumn => $srcColumn) { + $params[] = $identifier[$sourceClass->fieldNames[$srcColumn]]; + } + + return $params; + } + + /** + * {@inheritdoc} + */ + public function count(PersistentCollection $coll) + { + $mapping = $filterMapping = $coll->getMapping(); + $class = $this->_em->getClassMetadata($mapping['sourceEntity']); + $id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner()); + + if ($mapping['isOwningSide']) { + $joinColumns = $mapping['relationToSourceKeyColumns']; + } else { + $mapping = $this->_em->getClassMetadata($mapping['targetEntity'])->associationMappings[$mapping['mappedBy']]; + $joinColumns = $mapping['relationToTargetKeyColumns']; + } + + $whereClauses = array(); + $params = array(); + + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + if ( ! isset($joinColumns[$joinTableColumn])) { + continue; + } + + $whereClauses[] = $joinTableColumn . ' = ?'; + + $params[] = ($class->containsForeignIdentifier) + ? $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])] + : $id[$class->fieldNames[$joinColumns[$joinTableColumn]]]; + } + + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); + if ($filterSql) { + $whereClauses[] = $filterSql; + } + + $sql = 'SELECT COUNT(*)' + . ' FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform()) . ' t' + . $joinTargetEntitySQL + . ' WHERE ' . implode(' AND ', $whereClauses); + + return $this->_conn->fetchColumn($sql, $params); + } + + /** + * @param PersistentCollection $coll + * @param int $offset + * @param int $length + * @return array + */ + public function slice(PersistentCollection $coll, $offset, $length = null) + { + $mapping = $coll->getMapping(); + + return $this->_em->getUnitOfWork()->getEntityPersister($mapping['targetEntity'])->getManyToManyCollection($mapping, $coll->getOwner(), $offset, $length); + } + + /** + * @param PersistentCollection $coll + * @param object $element + * @return boolean + */ + public function contains(PersistentCollection $coll, $element) + { + $uow = $this->_em->getUnitOfWork(); + + // Shortcut for new entities + $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW); + + if ($entityState === UnitOfWork::STATE_NEW) { + return false; + } + + // Entity is scheduled for inclusion + if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) { + return false; + } + + list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($coll, $element, true); + + $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); + + return (bool) $this->_conn->fetchColumn($sql, $params); + } + + /** + * @param PersistentCollection $coll + * @param object $element + * @return boolean + */ + public function removeElement(PersistentCollection $coll, $element) + { + $uow = $this->_em->getUnitOfWork(); + + // shortcut for new entities + $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW); + + if ($entityState === UnitOfWork::STATE_NEW) { + return false; + } + + // If Entity is scheduled for inclusion, it is not in this collection. + // We can assure that because it would have return true before on array check + if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) { + return false; + } + + list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($coll, $element, false); + + $sql = 'DELETE FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); + + return (bool) $this->_conn->executeUpdate($sql, $params); + } + + /** + * @param \Doctrine\ORM\PersistentCollection $coll + * @param object $element + * @param boolean $addFilters Whether the filter SQL should be included or not. + * @return array + */ + private function getJoinTableRestrictions(PersistentCollection $coll, $element, $addFilters) + { + $uow = $this->_em->getUnitOfWork(); + $mapping = $filterMapping = $coll->getMapping(); + + if ( ! $mapping['isOwningSide']) { + $sourceClass = $this->_em->getClassMetadata($mapping['targetEntity']); + $targetClass = $this->_em->getClassMetadata($mapping['sourceEntity']); + $sourceId = $uow->getEntityIdentifier($element); + $targetId = $uow->getEntityIdentifier($coll->getOwner()); + + $mapping = $sourceClass->associationMappings[$mapping['mappedBy']]; + } else { + $sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']); + $targetClass = $this->_em->getClassMetadata($mapping['targetEntity']); + $sourceId = $uow->getEntityIdentifier($coll->getOwner()); + $targetId = $uow->getEntityIdentifier($element); + } + + $quotedJoinTable = $sourceClass->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform()); + $whereClauses = array(); + $params = array(); + + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + $whereClauses[] = $joinTableColumn . ' = ?'; + + if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { + $params[] = ($targetClass->containsForeignIdentifier) + ? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])] + : $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]]; + continue; + } + + // relationToSourceKeyColumns + $params[] = ($sourceClass->containsForeignIdentifier) + ? $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])] + : $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]]; + } + + if ($addFilters) { + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); + if ($filterSql) { + $quotedJoinTable .= ' t ' . $joinTargetEntitySQL; + $whereClauses[] = $filterSql; + } + } + + return array($quotedJoinTable, $whereClauses, $params); + } + + /** + * Generates the filter SQL for a given mapping. + * + * This method is not used for actually grabbing the related entities + * but when the extra-lazy collection methods are called on a filtered + * association. This is why besides the many to many table we also + * have to join in the actual entities table leading to additional + * JOIN. + * + * @param array $mapping Array containing mapping information. + * + * @return string The SQL query part to add to a query. + */ + public function getFilterSql($mapping) + { + $targetClass = $this->_em->getClassMetadata($mapping['targetEntity']); + + if ($mapping['isOwningSide']) { + $joinColumns = $mapping['relationToTargetKeyColumns']; + } else { + $mapping = $targetClass->associationMappings[$mapping['mappedBy']]; + $joinColumns = $mapping['relationToSourceKeyColumns']; + } + + $targetClass = $this->_em->getClassMetadata($targetClass->rootEntityName); + + // A join is needed if there is filtering on the target entity + $joinTargetEntitySQL = ''; + if ($filterSql = $this->generateFilterConditionSQL($targetClass, 'te')) { + $joinTargetEntitySQL = ' JOIN ' + . $targetClass->getQuotedTableName($this->_conn->getDatabasePlatform()) . ' te' + . ' ON'; + + $joinTargetEntitySQLClauses = array(); + foreach ($joinColumns as $joinTableColumn => $targetTableColumn) { + $joinTargetEntitySQLClauses[] = ' t.' . $joinTableColumn . ' = ' . 'te.' . $targetTableColumn; + } + + $joinTargetEntitySQL .= implode(' AND ', $joinTargetEntitySQLClauses); + } + + return array($joinTargetEntitySQL, $filterSql); + } + + /** + * Generates the filter SQL for a given entity and table alias. + * + * @param ClassMetadata $targetEntity Metadata of the target entity. + * @param string $targetTableAlias The table alias of the joined/selected table. + * + * @return string The SQL query part to add to a query. + */ + protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + { + $filterClauses = array(); + + foreach ($this->_em->getFilters()->getEnabledFilters() as $filter) { + if ($filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) { + $filterClauses[] = '(' . $filterExpr . ')'; + } + } + + $sql = implode(' AND ', $filterClauses); + return $sql ? "(" . $sql . ")" : ""; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/OneToManyPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/OneToManyPersister.php new file mode 100644 index 0000000..6f477f0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/OneToManyPersister.php @@ -0,0 +1,212 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\PersistentCollection, + Doctrine\ORM\UnitOfWork; + +/** + * Persister for one-to-many collections. + * + * @author Roman Borschel + * @author Guilherme Blanco + * @author Alexander + * @since 2.0 + */ +class OneToManyPersister extends AbstractCollectionPersister +{ + /** + * Generates the SQL UPDATE that updates a particular row's foreign + * key to null. + * + * @param PersistentCollection $coll + * @return string + * @override + */ + protected function _getDeleteRowSQL(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + $class = $this->_em->getClassMetadata($mapping['targetEntity']); + + return 'DELETE FROM ' . $class->getQuotedTableName($this->_conn->getDatabasePlatform()) + . ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?'; + } + + /** + * {@inheritdoc} + * + */ + protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element) + { + return array_values($this->_uow->getEntityIdentifier($element)); + } + + protected function _getInsertRowSQL(PersistentCollection $coll) + { + return "UPDATE xxx SET foreign_key = yyy WHERE foreign_key = zzz"; + } + + /** + * Gets the SQL parameters for the corresponding SQL statement to insert the given + * element of the given collection into the database. + * + * @param PersistentCollection $coll + * @param mixed $element + */ + protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element) + {} + + /* Not used for OneToManyPersister */ + protected function _getUpdateRowSQL(PersistentCollection $coll) + { + return; + } + + /** + * Generates the SQL UPDATE that updates all the foreign keys to null. + * + * @param PersistentCollection $coll + */ + protected function _getDeleteSQL(PersistentCollection $coll) + { + + } + + /** + * Gets the SQL parameters for the corresponding SQL statement to delete + * the given collection. + * + * @param PersistentCollection $coll + */ + protected function _getDeleteSQLParameters(PersistentCollection $coll) + {} + + /** + * {@inheritdoc} + */ + public function count(PersistentCollection $coll) + { + $mapping = $coll->getMapping(); + $targetClass = $this->_em->getClassMetadata($mapping['targetEntity']); + $sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']); + $id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner()); + + $whereClauses = array(); + $params = array(); + + foreach ($targetClass->associationMappings[$mapping['mappedBy']]['joinColumns'] AS $joinColumn) { + $whereClauses[] = $joinColumn['name'] . ' = ?'; + + $params[] = ($targetClass->containsForeignIdentifier) + ? $id[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])] + : $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]]; + } + + $filterTargetClass = $this->_em->getClassMetadata($targetClass->rootEntityName); + foreach ($this->_em->getFilters()->getEnabledFilters() as $filter) { + if ($filterExpr = $filter->addFilterConstraint($filterTargetClass, 't')) { + $whereClauses[] = '(' . $filterExpr . ')'; + } + } + + $sql = 'SELECT count(*)' + . ' FROM ' . $targetClass->getQuotedTableName($this->_conn->getDatabasePlatform()) . ' t' + . ' WHERE ' . implode(' AND ', $whereClauses); + + return $this->_conn->fetchColumn($sql, $params); + } + + /** + * @param PersistentCollection $coll + * @param int $offset + * @param int $length + * @return \Doctrine\Common\Collections\ArrayCollection + */ + public function slice(PersistentCollection $coll, $offset, $length = null) + { + $mapping = $coll->getMapping(); + $uow = $this->_em->getUnitOfWork(); + $persister = $uow->getEntityPersister($mapping['targetEntity']); + + return $persister->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length); + } + + /** + * @param PersistentCollection $coll + * @param object $element + * @return boolean + */ + public function contains(PersistentCollection $coll, $element) + { + $mapping = $coll->getMapping(); + $uow = $this->_em->getUnitOfWork(); + + // shortcut for new entities + $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW); + + if ($entityState === UnitOfWork::STATE_NEW) { + return false; + } + + // Entity is scheduled for inclusion + if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) { + return false; + } + + $persister = $uow->getEntityPersister($mapping['targetEntity']); + + // only works with single id identifier entities. Will throw an + // exception in Entity Persisters if that is not the case for the + // 'mappedBy' field. + $id = current( $uow->getEntityIdentifier($coll->getOwner())); + + return $persister->exists($element, array($mapping['mappedBy'] => $id)); + } + + /** + * @param PersistentCollection $coll + * @param object $element + * @return boolean + */ + public function removeElement(PersistentCollection $coll, $element) + { + $uow = $this->_em->getUnitOfWork(); + + // shortcut for new entities + $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW); + + if ($entityState === UnitOfWork::STATE_NEW) { + return false; + } + + // If Entity is scheduled for inclusion, it is not in this collection. + // We can assure that because it would have return true before on array check + if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) { + return false; + } + + $mapping = $coll->getMapping(); + $class = $this->_em->getClassMetadata($mapping['targetEntity']); + $sql = 'DELETE FROM ' . $class->getQuotedTableName($this->_conn->getDatabasePlatform()) + . ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?'; + + return (bool) $this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SingleTablePersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SingleTablePersister.php new file mode 100644 index 0000000..8644e1d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SingleTablePersister.php @@ -0,0 +1,146 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * Persister for entities that participate in a hierarchy mapped with the + * SINGLE_TABLE strategy. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Alexander + * @since 2.0 + * @link http://martinfowler.com/eaaCatalog/singleTableInheritance.html + */ +class SingleTablePersister extends AbstractEntityInheritancePersister +{ + /** {@inheritdoc} */ + protected function _getDiscriminatorColumnTableName() + { + return $this->_class->getTableName(); + } + + /** {@inheritdoc} */ + protected function _getSelectColumnListSQL() + { + if ($this->_selectColumnListSql !== null) { + return $this->_selectColumnListSql; + } + + $columnList = parent::_getSelectColumnListSQL(); + + $rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName); + $tableAlias = $this->_getSQLTableAlias($rootClass->name); + + // Append discriminator column + $discrColumn = $this->_class->discriminatorColumn['name']; + $columnList .= ', ' . $tableAlias . '.' . $discrColumn; + + $resultColumnName = $this->_platform->getSQLResultCasing($discrColumn); + + $this->_rsm->setDiscriminatorColumn('r', $resultColumnName); + $this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn); + + // Append subclass columns + foreach ($this->_class->subClasses as $subClassName) { + $subClass = $this->_em->getClassMetadata($subClassName); + + // Regular columns + foreach ($subClass->fieldMappings as $fieldName => $mapping) { + if ( ! isset($mapping['inherited'])) { + $columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass); + } + } + + // Foreign key columns + foreach ($subClass->associationMappings as $assoc) { + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) { + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { + if ($columnList != '') $columnList .= ', '; + + $columnList .= $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, + isset($assoc['inherited']) ? $assoc['inherited'] : $this->_class->name + ); + } + } + } + } + + $this->_selectColumnListSql = $columnList; + return $this->_selectColumnListSql; + } + + /** {@inheritdoc} */ + protected function _getInsertColumnList() + { + $columns = parent::_getInsertColumnList(); + + // Add discriminator column to the INSERT SQL + $columns[] = $this->_class->discriminatorColumn['name']; + + return $columns; + } + + /** {@inheritdoc} */ + protected function _getSQLTableAlias($className, $assocName = '') + { + return parent::_getSQLTableAlias($this->_class->rootEntityName, $assocName); + } + + /** {@inheritdoc} */ + protected function _getSelectConditionSQL(array $criteria, $assoc = null) + { + $conditionSql = parent::_getSelectConditionSQL($criteria, $assoc); + + // Append discriminator condition + if ($conditionSql) $conditionSql .= ' AND '; + + $values = array(); + + if ($this->_class->discriminatorValue !== null) { // discriminators can be 0 + $values[] = $this->_conn->quote($this->_class->discriminatorValue); + } + + $discrValues = array_flip($this->_class->discriminatorMap); + + foreach ($this->_class->subClasses as $subclassName) { + $values[] = $this->_conn->quote($discrValues[$subclassName]); + } + + $conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.' . $this->_class->discriminatorColumn['name'] + . ' IN (' . implode(', ', $values) . ')'; + + return $conditionSql; + } + + /** {@inheritdoc} */ + protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + { + // Ensure that the filters are applied to the root entity of the inheritance tree + $targetEntity = $this->_em->getClassMetadata($targetEntity->rootEntityName); + // we dont care about the $targetTableAlias, in a STI there is only one table. + + return parent::generateFilterConditionSQL($targetEntity, $targetTableAlias); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/UnionSubclassPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/UnionSubclassPersister.php new file mode 100644 index 0000000..ef844a7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/UnionSubclassPersister.php @@ -0,0 +1,8 @@ +. +*/ + +namespace Doctrine\ORM; + +/** + * Pessimistic Lock Exception + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class PessimisticLockException extends ORMException +{ + public static function lockFailed() + { + return new self("The pessimistic lock failed."); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Autoloader.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Autoloader.php new file mode 100644 index 0000000..876b3f2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Autoloader.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +/** + * Special Autoloader for Proxy classes because them not being PSR-0 compatible. + * + * @author Benjamin Eberlei + */ +class Autoloader +{ + /** + * Resolve proxy class name to a filename based on the following pattern. + * + * 1. Remove Proxy namespace from class name + * 2. Remove namespace seperators from remaining class name. + * 3. Return PHP filename from proxy-dir with the result from 2. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param string $className + * @return string + */ + static public function resolveFile($proxyDir, $proxyNamespace, $className) + { + if (0 !== strpos($className, $proxyNamespace)) { + throw ProxyException::notProxyClass($className, $proxyNamespace); + } + + $className = str_replace('\\', '', substr($className, strlen($proxyNamespace) +1)); + return $proxyDir . DIRECTORY_SEPARATOR . $className.'.php'; + } + + /** + * Register and return autoloader callback for the given proxy dir and + * namespace. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param Closure $notFoundCallback Invoked when the proxy file is not found. + * @return Closure + */ + static public function register($proxyDir, $proxyNamespace, \Closure $notFoundCallback = null) + { + $proxyNamespace = ltrim($proxyNamespace, "\\"); + $autoloader = function($className) use ($proxyDir, $proxyNamespace, $notFoundCallback) { + if (0 === strpos($className, $proxyNamespace)) { + $file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className); + + if ($notFoundCallback && ! file_exists($file)) { + $notFoundCallback($proxyDir, $proxyNamespace, $className); + } + + require $file; + } + }; + spl_autoload_register($autoloader); + return $autoloader; + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Proxy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Proxy.php new file mode 100644 index 0000000..09e2b33 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Proxy.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +use Doctrine\Common\Persistence\Proxy as BaseProxy; + +/** + * Interface for proxy classes. + * + * @author Roman Borschel + * @since 2.0 + */ +interface Proxy extends BaseProxy {} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyException.php new file mode 100644 index 0000000..2196412 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyException.php @@ -0,0 +1,51 @@ +. +*/ + +namespace Doctrine\ORM\Proxy; + +/** + * ORM Proxy Exception + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class ProxyException extends \Doctrine\ORM\ORMException { + + public static function proxyDirectoryRequired() { + return new self("You must configure a proxy directory. See docs for details"); + } + + public static function proxyNamespaceRequired() { + return new self("You must configure a proxy namespace. See docs for details"); + } + + public static function notProxyClass($className, $proxyNamespace) + { + return new self(sprintf( + "The class %s is not part of the proxy namespace %s", + $className, $proxyNamespace + )); + } + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php new file mode 100644 index 0000000..be94c1d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -0,0 +1,403 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +use Doctrine\ORM\EntityManager, + Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\Mapping\AssociationMapping, + Doctrine\Common\Util\ClassUtils; + +/** + * This factory is used to create proxy objects for entities at runtime. + * + * @author Roman Borschel + * @author Giorgio Sironi + * @since 2.0 + */ +class ProxyFactory +{ + /** The EntityManager this factory is bound to. */ + private $_em; + /** Whether to automatically (re)generate proxy classes. */ + private $_autoGenerate; + /** The namespace that contains all proxy classes. */ + private $_proxyNamespace; + /** The directory that contains all proxy classes. */ + private $_proxyDir; + + /** + * Used to match very simple id methods that don't need + * to be proxied since the identifier is known. + * + * @var string + */ + const PATTERN_MATCH_ID_METHOD = '((public\s)?(function\s{1,}%s\s?\(\)\s{1,})\s{0,}{\s{0,}return\s{0,}\$this->%s;\s{0,}})i'; + + /** + * Initializes a new instance of the ProxyFactory class that is + * connected to the given EntityManager. + * + * @param EntityManager $em The EntityManager the new factory works for. + * @param string $proxyDir The directory to use for the proxy classes. It must exist. + * @param string $proxyNs The namespace to use for the proxy classes. + * @param boolean $autoGenerate Whether to automatically generate proxy classes. + */ + public function __construct(EntityManager $em, $proxyDir, $proxyNs, $autoGenerate = false) + { + if ( ! $proxyDir) { + throw ProxyException::proxyDirectoryRequired(); + } + if ( ! $proxyNs) { + throw ProxyException::proxyNamespaceRequired(); + } + $this->_em = $em; + $this->_proxyDir = $proxyDir; + $this->_autoGenerate = $autoGenerate; + $this->_proxyNamespace = $proxyNs; + } + + /** + * Gets a reference proxy instance for the entity of the given type and identified by + * the given identifier. + * + * @param string $className + * @param mixed $identifier + * @return object + */ + public function getProxy($className, $identifier) + { + $fqn = ClassUtils::generateProxyClassName($className, $this->_proxyNamespace); + + if (! class_exists($fqn, false)) { + $fileName = $this->getProxyFileName($className); + if ($this->_autoGenerate) { + $this->_generateProxyClass($this->_em->getClassMetadata($className), $fileName, self::$_proxyClassTemplate); + } + require $fileName; + } + + if ( ! $this->_em->getMetadataFactory()->hasMetadataFor($fqn)) { + $this->_em->getMetadataFactory()->setMetadataFor($fqn, $this->_em->getClassMetadata($className)); + } + + $entityPersister = $this->_em->getUnitOfWork()->getEntityPersister($className); + + return new $fqn($entityPersister, $identifier); + } + + /** + * Generate the Proxy file name + * + * @param string $className + * @param string $baseDir Optional base directory for proxy file name generation. + * If not specified, the directory configured on the Configuration of the + * EntityManager will be used by this factory. + * @return string + */ + private function getProxyFileName($className, $baseDir = null) + { + $proxyDir = $baseDir ?: $this->_proxyDir; + + return $proxyDir . DIRECTORY_SEPARATOR . '__CG__' . str_replace('\\', '', $className) . '.php'; + } + + /** + * Generates proxy classes for all given classes. + * + * @param array $classes The classes (ClassMetadata instances) for which to generate proxies. + * @param string $toDir The target directory of the proxy classes. If not specified, the + * directory configured on the Configuration of the EntityManager used + * by this factory is used. + * @return int Number of generated proxies. + */ + public function generateProxyClasses(array $classes, $toDir = null) + { + $proxyDir = $toDir ?: $this->_proxyDir; + $proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR); + $num = 0; + + foreach ($classes as $class) { + /* @var $class ClassMetadata */ + if ($class->isMappedSuperclass || $class->reflClass->isAbstract()) { + continue; + } + + $proxyFileName = $this->getProxyFileName($class->name, $proxyDir); + + $this->_generateProxyClass($class, $proxyFileName, self::$_proxyClassTemplate); + $num++; + } + + return $num; + } + + /** + * Generates a proxy class file. + * + * @param $class + * @param $proxyClassName + * @param $file The path of the file to write to. + */ + private function _generateProxyClass($class, $fileName, $file) + { + $methods = $this->_generateMethods($class); + $sleepImpl = $this->_generateSleep($class); + $cloneImpl = $class->reflClass->hasMethod('__clone') ? 'parent::__clone();' : ''; // hasMethod() checks case-insensitive + + $placeholders = array( + '', + '', '', + '', '', '' + ); + + $className = ltrim($class->name, '\\'); + $proxyClassName = ClassUtils::generateProxyClassName($class->name, $this->_proxyNamespace); + $parts = explode('\\', strrev($proxyClassName), 2); + $proxyClassNamespace = strrev($parts[1]); + $proxyClassName = strrev($parts[0]); + + $replacements = array( + $proxyClassNamespace, + $proxyClassName, + $className, + $methods, + $sleepImpl, + $cloneImpl + ); + + $file = str_replace($placeholders, $replacements, $file); + + file_put_contents($fileName, $file, LOCK_EX); + } + + /** + * Generates the methods of a proxy class. + * + * @param ClassMetadata $class + * @return string The code of the generated methods. + */ + private function _generateMethods(ClassMetadata $class) + { + $methods = ''; + + $methodNames = array(); + foreach ($class->reflClass->getMethods() as $method) { + /* @var $method ReflectionMethod */ + if ($method->isConstructor() || in_array(strtolower($method->getName()), array("__sleep", "__clone")) || isset($methodNames[$method->getName()])) { + continue; + } + $methodNames[$method->getName()] = true; + + if ($method->isPublic() && ! $method->isFinal() && ! $method->isStatic()) { + $methods .= "\n" . ' public function '; + if ($method->returnsReference()) { + $methods .= '&'; + } + $methods .= $method->getName() . '('; + $firstParam = true; + $parameterString = $argumentString = ''; + + foreach ($method->getParameters() as $param) { + if ($firstParam) { + $firstParam = false; + } else { + $parameterString .= ', '; + $argumentString .= ', '; + } + + // We need to pick the type hint class too + if (($paramClass = $param->getClass()) !== null) { + $parameterString .= '\\' . $paramClass->getName() . ' '; + } else if ($param->isArray()) { + $parameterString .= 'array '; + } + + if ($param->isPassedByReference()) { + $parameterString .= '&'; + } + + $parameterString .= '$' . $param->getName(); + $argumentString .= '$' . $param->getName(); + + if ($param->isDefaultValueAvailable()) { + $parameterString .= ' = ' . var_export($param->getDefaultValue(), true); + } + } + + $methods .= $parameterString . ')'; + $methods .= "\n" . ' {' . "\n"; + if ($this->isShortIdentifierGetter($method, $class)) { + $identifier = lcfirst(substr($method->getName(), 3)); + + $cast = in_array($class->fieldMappings[$identifier]['type'], array('integer', 'smallint')) ? '(int) ' : ''; + + $methods .= ' if ($this->__isInitialized__ === false) {' . "\n"; + $methods .= ' return ' . $cast . '$this->_identifier["' . $identifier . '"];' . "\n"; + $methods .= ' }' . "\n"; + } + $methods .= ' $this->__load();' . "\n"; + $methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');'; + $methods .= "\n" . ' }' . "\n"; + } + } + + return $methods; + } + + /** + * Check if the method is a short identifier getter. + * + * What does this mean? For proxy objects the identifier is already known, + * however accessing the getter for this identifier usually triggers the + * lazy loading, leading to a query that may not be necessary if only the + * ID is interesting for the userland code (for example in views that + * generate links to the entity, but do not display anything else). + * + * @param ReflectionMethod $method + * @param ClassMetadata $class + * @return bool + */ + private function isShortIdentifierGetter($method, $class) + { + $identifier = lcfirst(substr($method->getName(), 3)); + $cheapCheck = ( + $method->getNumberOfParameters() == 0 && + substr($method->getName(), 0, 3) == "get" && + in_array($identifier, $class->identifier, true) && + $class->hasField($identifier) && + (($method->getEndLine() - $method->getStartLine()) <= 4) + && in_array($class->fieldMappings[$identifier]['type'], array('integer', 'bigint', 'smallint', 'string')) + ); + + if ($cheapCheck) { + $code = file($method->getDeclaringClass()->getFileName()); + $code = trim(implode(" ", array_slice($code, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1))); + + $pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier); + + if (preg_match($pattern, $code)) { + return true; + } + } + return false; + } + + /** + * Generates the code for the __sleep method for a proxy class. + * + * @param $class + * @return string + */ + private function _generateSleep(ClassMetadata $class) + { + $sleepImpl = ''; + + if ($class->reflClass->hasMethod('__sleep')) { + $sleepImpl .= "return array_merge(array('__isInitialized__'), parent::__sleep());"; + } else { + $sleepImpl .= "return array('__isInitialized__', "; + $first = true; + + foreach ($class->getReflectionProperties() as $name => $prop) { + if ($first) { + $first = false; + } else { + $sleepImpl .= ', '; + } + + $sleepImpl .= "'" . $name . "'"; + } + + $sleepImpl .= ');'; + } + + return $sleepImpl; + } + + /** Proxy class code template */ + private static $_proxyClassTemplate = +'; + +/** + * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE. + */ +class extends \ implements \Doctrine\ORM\Proxy\Proxy +{ + private $_entityPersister; + private $_identifier; + public $__isInitialized__ = false; + public function __construct($entityPersister, $identifier) + { + $this->_entityPersister = $entityPersister; + $this->_identifier = $identifier; + } + /** @private */ + public function __load() + { + if (!$this->__isInitialized__ && $this->_entityPersister) { + $this->__isInitialized__ = true; + + if (method_exists($this, "__wakeup")) { + // call this after __isInitialized__to avoid infinite recursion + // but before loading to emulate what ClassMetadata::newInstance() + // provides. + $this->__wakeup(); + } + + if ($this->_entityPersister->load($this->_identifier, $this) === null) { + throw new \Doctrine\ORM\EntityNotFoundException(); + } + unset($this->_entityPersister, $this->_identifier); + } + } + + /** @private */ + public function __isInitialized() + { + return $this->__isInitialized__; + } + + + + public function __sleep() + { + + } + + public function __clone() + { + if (!$this->__isInitialized__ && $this->_entityPersister) { + $this->__isInitialized__ = true; + $class = $this->_entityPersister->getClassMetadata(); + $original = $this->_entityPersister->load($this->_identifier); + if ($original === null) { + throw new \Doctrine\ORM\EntityNotFoundException(); + } + foreach ($class->reflFields AS $field => $reflProperty) { + $reflProperty->setValue($this, $reflProperty->getValue($original)); + } + unset($this->_entityPersister, $this->_identifier); + } + + } +}'; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php new file mode 100644 index 0000000..16c6c38 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php @@ -0,0 +1,614 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\DBAL\LockMode, + Doctrine\ORM\Query\Parser, + Doctrine\ORM\Query\QueryException; + +/** + * A Query object represents a DQL query. + * + * @since 1.0 + * @author Guilherme Blanco + * @author Konsta Vesterinen + * @author Roman Borschel + */ +final class Query extends AbstractQuery +{ + /* Query STATES */ + /** + * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts. + */ + const STATE_CLEAN = 1; + /** + * A query object is in state DIRTY when it has DQL parts that have not yet been + * parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart + * is called. + */ + const STATE_DIRTY = 2; + + /* Query HINTS */ + /** + * The refresh hint turns any query into a refresh query with the result that + * any local changes in entities are overridden with the fetched values. + * + * @var string + */ + const HINT_REFRESH = 'doctrine.refresh'; + + + /** + * Internal hint: is set to the proxy entity that is currently triggered for loading + * + * @var string + */ + const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity'; + + /** + * The forcePartialLoad query hint forces a particular query to return + * partial objects. + * + * @var string + * @todo Rename: HINT_OPTIMIZE + */ + const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad'; + /** + * The includeMetaColumns query hint causes meta columns like foreign keys and + * discriminator columns to be selected and returned as part of the query result. + * + * This hint does only apply to non-object queries. + * + * @var string + */ + const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns'; + + /** + * An array of class names that implement \Doctrine\ORM\Query\TreeWalker and + * are iterated and executed after the DQL has been parsed into an AST. + * + * @var string + */ + const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers'; + + /** + * A string with a class name that implements \Doctrine\ORM\Query\TreeWalker + * and is used for generating the target SQL from any DQL AST tree. + * + * @var string + */ + const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker'; + + //const HINT_READ_ONLY = 'doctrine.readOnly'; + + /** + * @var string + */ + const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration'; + + /** + * @var string + */ + const HINT_LOCK_MODE = 'doctrine.lockMode'; + + /** + * @var integer $_state The current state of this query. + */ + private $_state = self::STATE_CLEAN; + + /** + * @var string $_dql Cached DQL query. + */ + private $_dql = null; + + /** + * @var \Doctrine\ORM\Query\ParserResult The parser result that holds DQL => SQL information. + */ + private $_parserResult; + + /** + * @var integer The first result to return (the "offset"). + */ + private $_firstResult = null; + + /** + * @var integer The maximum number of results to return (the "limit"). + */ + private $_maxResults = null; + + /** + * @var CacheDriver The cache driver used for caching queries. + */ + private $_queryCache; + + /** + * @var boolean Boolean value that indicates whether or not expire the query cache. + */ + private $_expireQueryCache = false; + + /** + * @var int Query Cache lifetime. + */ + private $_queryCacheTTL; + + /** + * @var boolean Whether to use a query cache, if available. Defaults to TRUE. + */ + private $_useQueryCache = true; + + // End of Caching Stuff + + /** + * Initializes a new Query instance. + * + * @param \Doctrine\ORM\EntityManager $entityManager + */ + /*public function __construct(EntityManager $entityManager) + { + parent::__construct($entityManager); + }*/ + + /** + * Gets the SQL query/queries that correspond to this DQL query. + * + * @return mixed The built sql query or an array of all sql queries. + * @override + */ + public function getSQL() + { + return $this->_parse()->getSQLExecutor()->getSQLStatements(); + } + + /** + * Returns the corresponding AST for this DQL query. + * + * @return \Doctrine\ORM\Query\AST\SelectStatement | + * \Doctrine\ORM\Query\AST\UpdateStatement | + * \Doctrine\ORM\Query\AST\DeleteStatement + */ + public function getAST() + { + $parser = new Parser($this); + return $parser->getAST(); + } + + /** + * Parses the DQL query, if necessary, and stores the parser result. + * + * Note: Populates $this->_parserResult as a side-effect. + * + * @return \Doctrine\ORM\Query\ParserResult + */ + private function _parse() + { + // Return previous parser result if the query and the filter collection are both clean + if ($this->_state === self::STATE_CLEAN + && $this->_em->isFiltersStateClean() + ) { + return $this->_parserResult; + } + + $this->_state = self::STATE_CLEAN; + + // Check query cache. + if ( ! ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver()))) { + $parser = new Parser($this); + $this->_parserResult = $parser->parse(); + + return $this->_parserResult; + } + + $hash = $this->_getQueryCacheId(); + $cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash); + + if ($cached !== false) { + // Cache hit. + $this->_parserResult = $cached; + + return $this->_parserResult; + } + + // Cache miss. + $parser = new Parser($this); + $this->_parserResult = $parser->parse(); + $queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL); + + return $this->_parserResult; + } + + /** + * {@inheritdoc} + */ + protected function _doExecute() + { + $executor = $this->_parse()->getSqlExecutor(); + + if ($this->_queryCacheProfile) { + $executor->setQueryCacheProfile($this->_queryCacheProfile); + } + + // Prepare parameters + $paramMappings = $this->_parserResult->getParameterMappings(); + + if (count($paramMappings) != count($this->_params)) { + throw QueryException::invalidParameterNumber(); + } + + list($sqlParams, $types) = $this->processParameterMappings($paramMappings); + + if ($this->_resultSetMapping === null) { + $this->_resultSetMapping = $this->_parserResult->getResultSetMapping(); + } + + return $executor->execute($this->_em->getConnection(), $sqlParams, $types); + } + + /** + * Processes query parameter mappings + * + * @param array $paramMappings + * @return array + */ + private function processParameterMappings($paramMappings) + { + $sqlParams = $types = array(); + + foreach ($this->_params as $key => $value) { + if ( ! isset($paramMappings[$key])) { + throw QueryException::unknownParameter($key); + } + + if (isset($this->_paramTypes[$key])) { + foreach ($paramMappings[$key] as $position) { + $types[$position] = $this->_paramTypes[$key]; + } + } + + $sqlPositions = $paramMappings[$key]; + // optimized multi value sql positions away for now, they are not allowed in DQL anyways. + $value = array($value); + $countValue = count($value); + + for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) { + $sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)]; + } + } + + if (count($sqlParams) != count($types)) { + throw QueryException::parameterTypeMissmatch(); + } + + if ($sqlParams) { + ksort($sqlParams); + $sqlParams = array_values($sqlParams); + + ksort($types); + $types = array_values($types); + } + + return array($sqlParams, $types); + } + + /** + * Defines a cache driver to be used for caching queries. + * + * @param Doctrine_Cache_Interface|null $driver Cache driver + * @return Query This query instance. + */ + public function setQueryCacheDriver($queryCache) + { + $this->_queryCache = $queryCache; + + return $this; + } + + /** + * Defines whether the query should make use of a query cache, if available. + * + * @param boolean $bool + * @return @return Query This query instance. + */ + public function useQueryCache($bool) + { + $this->_useQueryCache = $bool; + + return $this; + } + + /** + * Returns the cache driver used for query caching. + * + * @return CacheDriver The cache driver used for query caching or NULL, if this + * Query does not use query caching. + */ + public function getQueryCacheDriver() + { + if ($this->_queryCache) { + return $this->_queryCache; + } + + return $this->_em->getConfiguration()->getQueryCacheImpl(); + } + + /** + * Defines how long the query cache will be active before expire. + * + * @param integer $timeToLive How long the cache entry is valid + * @return Query This query instance. + */ + public function setQueryCacheLifetime($timeToLive) + { + if ($timeToLive !== null) { + $timeToLive = (int) $timeToLive; + } + + $this->_queryCacheTTL = $timeToLive; + + return $this; + } + + /** + * Retrieves the lifetime of resultset cache. + * + * @return int + */ + public function getQueryCacheLifetime() + { + return $this->_queryCacheTTL; + } + + /** + * Defines if the query cache is active or not. + * + * @param boolean $expire Whether or not to force query cache expiration. + * @return Query This query instance. + */ + public function expireQueryCache($expire = true) + { + $this->_expireQueryCache = $expire; + + return $this; + } + + /** + * Retrieves if the query cache is active or not. + * + * @return bool + */ + public function getExpireQueryCache() + { + return $this->_expireQueryCache; + } + + /** + * @override + */ + public function free() + { + parent::free(); + + $this->_dql = null; + $this->_state = self::STATE_CLEAN; + } + + /** + * Sets a DQL query string. + * + * @param string $dqlQuery DQL Query + * @return \Doctrine\ORM\AbstractQuery + */ + public function setDQL($dqlQuery) + { + if ($dqlQuery !== null) { + $this->_dql = $dqlQuery; + $this->_state = self::STATE_DIRTY; + } + + return $this; + } + + /** + * Returns the DQL query that is represented by this query object. + * + * @return string DQL query + */ + public function getDQL() + { + return $this->_dql; + } + + /** + * Returns the state of this query object + * By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL + * part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY. + * + * @see AbstractQuery::STATE_CLEAN + * @see AbstractQuery::STATE_DIRTY + * + * @return integer Return the query state + */ + public function getState() + { + return $this->_state; + } + + /** + * Method to check if an arbitrary piece of DQL exists + * + * @param string $dql Arbitrary piece of DQL to check for + * @return boolean + */ + public function contains($dql) + { + return stripos($this->getDQL(), $dql) === false ? false : true; + } + + /** + * Sets the position of the first result to retrieve (the "offset"). + * + * @param integer $firstResult The first result to return. + * @return Query This query object. + */ + public function setFirstResult($firstResult) + { + $this->_firstResult = $firstResult; + $this->_state = self::STATE_DIRTY; + + return $this; + } + + /** + * Gets the position of the first result the query object was set to retrieve (the "offset"). + * Returns NULL if {@link setFirstResult} was not applied to this query. + * + * @return integer The position of the first result. + */ + public function getFirstResult() + { + return $this->_firstResult; + } + + /** + * Sets the maximum number of results to retrieve (the "limit"). + * + * @param integer $maxResults + * @return Query This query object. + */ + public function setMaxResults($maxResults) + { + $this->_maxResults = $maxResults; + $this->_state = self::STATE_DIRTY; + + return $this; + } + + /** + * Gets the maximum number of results the query object was set to retrieve (the "limit"). + * Returns NULL if {@link setMaxResults} was not applied to this query. + * + * @return integer Maximum number of results. + */ + public function getMaxResults() + { + return $this->_maxResults; + } + + /** + * Executes the query and returns an IterableResult that can be used to incrementally + * iterated over the result. + * + * @param array $params The query parameters. + * @param integer $hydrationMode The hydration mode to use. + * @return \Doctrine\ORM\Internal\Hydration\IterableResult + */ + public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT) + { + $this->setHint(self::HINT_INTERNAL_ITERATION, true); + + return parent::iterate($params, $hydrationMode); + } + + /** + * {@inheritdoc} + */ + public function setHint($name, $value) + { + $this->_state = self::STATE_DIRTY; + + return parent::setHint($name, $value); + } + + /** + * {@inheritdoc} + */ + public function setHydrationMode($hydrationMode) + { + $this->_state = self::STATE_DIRTY; + + return parent::setHydrationMode($hydrationMode); + } + + /** + * Set the lock mode for this Query. + * + * @see \Doctrine\DBAL\LockMode + * @param int $lockMode + * @return Query + */ + public function setLockMode($lockMode) + { + if ($lockMode === LockMode::PESSIMISTIC_READ || $lockMode === LockMode::PESSIMISTIC_WRITE) { + if ( ! $this->_em->getConnection()->isTransactionActive()) { + throw TransactionRequiredException::transactionRequired(); + } + } + + $this->setHint(self::HINT_LOCK_MODE, $lockMode); + + return $this; + } + + /** + * Get the current lock mode for this query. + * + * @return int + */ + public function getLockMode() + { + $lockMode = $this->getHint(self::HINT_LOCK_MODE); + + if ( ! $lockMode) { + return LockMode::NONE; + } + + return $lockMode; + } + + /** + * Generate a cache id for the query cache - reusing the Result-Cache-Id generator. + * + * The query cache + * + * @return string + */ + protected function _getQueryCacheId() + { + ksort($this->_hints); + + return md5( + $this->getDql() . var_export($this->_hints, true) . + ($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') . + '&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults . + '&hydrationMode='.$this->_hydrationMode.'DOCTRINE_QUERY_CACHE_SALT' + ); + } + + /** + * Cleanup Query resource when clone is called. + * + * @return void + */ + public function __clone() + { + parent::__clone(); + + $this->_state = self::STATE_DIRTY; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ASTException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ASTException.php new file mode 100644 index 0000000..7472572 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ASTException.php @@ -0,0 +1,13 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of AggregateExpression + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class AggregateExpression extends Node +{ + public $functionName; + public $pathExpression; + public $isDistinct = false; // Some aggregate expressions support distinct, eg COUNT + + public function __construct($functionName, $pathExpression, $isDistinct) + { + $this->functionName = $functionName; + $this->pathExpression = $pathExpression; + $this->isDistinct = $isDistinct; + } + + public function dispatch($walker) + { + return $walker->walkAggregateExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php new file mode 100644 index 0000000..679cbcc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArithmeticExpression extends Node +{ + public $simpleArithmeticExpression; + public $subselect; + + public function isSimpleArithmeticExpression() + { + return (bool) $this->simpleArithmeticExpression; + } + + public function isSubselect() + { + return (bool) $this->subselect; + } + + public function dispatch($walker) + { + return $walker->walkArithmeticExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticFactor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticFactor.php new file mode 100644 index 0000000..3ad6abf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticFactor.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArithmeticFactor extends Node +{ + /** + * @var ArithmeticPrimary + */ + public $arithmeticPrimary; + + /** + * @var null|boolean NULL represents no sign, TRUE means positive and FALSE means negative sign + */ + public $sign; + + public function __construct($arithmeticPrimary, $sign = null) + { + $this->arithmeticPrimary = $arithmeticPrimary; + $this->sign = $sign; + } + + public function isPositiveSigned() + { + return $this->sign === true; + } + + public function isNegativeSigned() + { + return $this->sign === false; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkArithmeticFactor($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticTerm.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticTerm.php new file mode 100644 index 0000000..a233e05 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticTerm.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}* + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArithmeticTerm extends Node +{ + public $arithmeticFactors; + + public function __construct(array $arithmeticFactors) + { + $this->arithmeticFactors = $arithmeticFactors; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkArithmeticTerm($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/BetweenExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/BetweenExpression.php new file mode 100644 index 0000000..c00bca4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/BetweenExpression.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of BetweenExpression + * + @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class BetweenExpression extends Node +{ + public $expression; + public $leftBetweenExpression; + public $rightBetweenExpression; + public $not; + + public function __construct($expr, $leftExpr, $rightExpr) + { + $this->expression = $expr; + $this->leftBetweenExpression = $leftExpr; + $this->rightBetweenExpression = $rightExpr; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkBetweenExpression($this); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CoalesceExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CoalesceExpression.php new file mode 100644 index 0000000..dae0742 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CoalesceExpression.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" + * + * @since 2.1 + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class CoalesceExpression extends Node +{ + public $scalarExpressions = array(); + + + public function __construct(array $scalarExpressions) + { + $this->scalarExpressions = $scalarExpressions; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkCoalesceExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CollectionMemberExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CollectionMemberExpression.php new file mode 100644 index 0000000..62d756b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CollectionMemberExpression.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class CollectionMemberExpression extends Node +{ + public $entityExpression; + public $collectionValuedPathExpression; + public $not; + + public function __construct($entityExpr, $collValuedPathExpr) + { + $this->entityExpression = $entityExpr; + $this->collectionValuedPathExpression = $collValuedPathExpr; + } + + public function dispatch($walker) + { + return $walker->walkCollectionMemberExpression($this); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ComparisonExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ComparisonExpression.php new file mode 100644 index 0000000..a13ae26 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ComparisonExpression.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) | + * StringExpression ComparisonOperator (StringExpression | QuantifiedExpression) | + * BooleanExpression ("=" | "<>" | "!=") (BooleanExpression | QuantifiedExpression) | + * EnumExpression ("=" | "<>" | "!=") (EnumExpression | QuantifiedExpression) | + * DatetimeExpression ComparisonOperator (DatetimeExpression | QuantifiedExpression) | + * EntityExpression ("=" | "<>") (EntityExpression | QuantifiedExpression) + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ComparisonExpression extends Node +{ + public $leftExpression; + public $rightExpression; + public $operator; + + public function __construct($leftExpr, $operator, $rightExpr) + { + $this->leftExpression = $leftExpr; + $this->rightExpression = $rightExpr; + $this->operator = $operator; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkComparisonExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalExpression.php new file mode 100644 index 0000000..d2e7762 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalExpression.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}* + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalExpression extends Node +{ + public $conditionalTerms = array(); + + public function __construct(array $conditionalTerms) + { + $this->conditionalTerms = $conditionalTerms; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalFactor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalFactor.php new file mode 100644 index 0000000..f0b165b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalFactor.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalFactor ::= ["NOT"] ConditionalPrimary + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalFactor extends Node +{ + public $not = false; + public $conditionalPrimary; + + public function __construct($conditionalPrimary) + { + $this->conditionalPrimary = $conditionalPrimary; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalFactor($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalPrimary.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalPrimary.php new file mode 100644 index 0000000..0e3f127 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalPrimary.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalPrimary extends Node +{ + public $simpleConditionalExpression; + public $conditionalExpression; + + public function isSimpleConditionalExpression() + { + return (bool) $this->simpleConditionalExpression; + } + + public function isConditionalExpression() + { + return (bool) $this->conditionalExpression; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalPrimary($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalTerm.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalTerm.php new file mode 100644 index 0000000..78ba6a2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalTerm.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}* + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalTerm extends Node +{ + public $conditionalFactors = array(); + + public function __construct(array $conditionalFactors) + { + $this->conditionalFactors = $conditionalFactors; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalTerm($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteClause.php new file mode 100644 index 0000000..4acf49c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteClause.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName [["AS"] AliasIdentificationVariable] + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DeleteClause extends Node +{ + public $abstractSchemaName; + public $aliasIdentificationVariable; + + public function __construct($abstractSchemaName) + { + $this->abstractSchemaName = $abstractSchemaName; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkDeleteClause($this); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteStatement.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteStatement.php new file mode 100644 index 0000000..01c6acb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteStatement.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * DeleteStatement = DeleteClause [WhereClause] + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DeleteStatement extends Node +{ + public $deleteClause; + public $whereClause; + + public function __construct($deleteClause) + { + $this->deleteClause = $deleteClause; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkDeleteStatement($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/EmptyCollectionComparisonExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/EmptyCollectionComparisonExpression.php new file mode 100644 index 0000000..6bb50e8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/EmptyCollectionComparisonExpression.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EmptyCollectionComparisonExpression extends Node +{ + public $expression; + public $not; + + public function __construct($expression) + { + $this->expression = $expression; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkEmptyCollectionComparisonExpression($this); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ExistsExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ExistsExpression.php new file mode 100644 index 0000000..3d9ad20 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ExistsExpression.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ExistsExpression extends Node +{ + public $not; + public $subselect; + + public function __construct($subselect) + { + $this->subselect = $subselect; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkExistsExpression($this); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/FromClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/FromClause.php new file mode 100644 index 0000000..a4dc628 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/FromClause.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration} + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class FromClause extends Node +{ + public $identificationVariableDeclarations = array(); + + public function __construct(array $identificationVariableDeclarations) + { + $this->identificationVariableDeclarations = $identificationVariableDeclarations; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkFromClause($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/AbsFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/AbsFunction.php new file mode 100644 index 0000000..3ee4360 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/AbsFunction.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "ABS" "(" SimpleArithmeticExpression ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class AbsFunction extends FunctionNode +{ + public $simpleArithmeticExpression; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return 'ABS(' . $sqlWalker->walkSimpleArithmeticExpression( + $this->simpleArithmeticExpression + ) . ')'; + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitAndFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitAndFunction.php new file mode 100644 index 0000000..1ee8233 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitAndFunction.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabio B. Silva + */ +class BitAndFunction extends FunctionNode +{ + public $firstArithmetic; + public $secondArithmetic; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + return $platform->getBitAndComparisonExpression( + $this->firstArithmetic->dispatch($sqlWalker), + $this->secondArithmetic->dispatch($sqlWalker) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstArithmetic = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->secondArithmetic = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitOrFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitOrFunction.php new file mode 100644 index 0000000..ba36e97 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitOrFunction.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabio B. Silva + */ +class BitOrFunction extends FunctionNode +{ + public $firstArithmetic; + public $secondArithmetic; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + return $platform->getBitOrComparisonExpression( + $this->firstArithmetic->dispatch($sqlWalker), + $this->secondArithmetic->dispatch($sqlWalker) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstArithmetic = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->secondArithmetic = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php new file mode 100644 index 0000000..bb2333a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "CONCAT" "(" StringPrimary "," StringPrimary ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class ConcatFunction extends FunctionNode +{ + public $firstStringPrimary; + public $secondStringPriamry; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + return $platform->getConcatExpression( + $sqlWalker->walkStringPrimary($this->firstStringPrimary), + $sqlWalker->walkStringPrimary($this->secondStringPrimary) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstStringPrimary = $parser->StringPrimary(); + $parser->match(Lexer::T_COMMA); + $this->secondStringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentDateFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentDateFunction.php new file mode 100644 index 0000000..66107cc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentDateFunction.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "CURRENT_DATE" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class CurrentDateFunction extends FunctionNode +{ + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentDateSQL(); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimeFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimeFunction.php new file mode 100644 index 0000000..1335755 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimeFunction.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "CURRENT_TIME" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class CurrentTimeFunction extends FunctionNode +{ + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentTimeSQL(); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimestampFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimestampFunction.php new file mode 100644 index 0000000..7b1a25d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimestampFunction.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "CURRENT_TIMESTAMP" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class CurrentTimestampFunction extends FunctionNode +{ + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentTimestampSQL(); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php new file mode 100644 index 0000000..1d840cc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\QueryException; + +/** + * "DATE_ADD(date1, interval, unit)" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DateAddFunction extends FunctionNode +{ + public $firstDateExpression = null; + public $intervalExpression = null; + public $unit = null; + + public function getSql(SqlWalker $sqlWalker) + { + $unit = strtolower($this->unit); + if ($unit == "day") { + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddDaysExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + } else if ($unit == "month") { + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddMonthExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + } else { + throw QueryException::semanticalError('DATE_ADD() only supports units of type day and month.'); + } + } + + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstDateExpression = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->intervalExpression = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->unit = $parser->StringPrimary(); + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateDiffFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateDiffFunction.php new file mode 100644 index 0000000..4de5411 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateDiffFunction.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\Parser; + +/** + * "DATE_DIFF(date1, date2)" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DateDiffFunction extends FunctionNode +{ + public $date1; + public $date2; + + public function getSql(SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateDiffExpression( + $this->date1->dispatch($sqlWalker), + $this->date2->dispatch($sqlWalker) + ); + } + + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->date1 = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->date2 = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php new file mode 100644 index 0000000..a608d49 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\QueryException; + +/** + * "DATE_ADD(date1, interval, unit)" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DateSubFunction extends DateAddFunction +{ + public $firstDateExpression = null; + public $intervalExpression = null; + public $unit = null; + + public function getSql(SqlWalker $sqlWalker) + { + $unit = strtolower($this->unit); + if ($unit == "day") { + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubDaysExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + } else if ($unit == "month") { + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubMonthExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + } else { + throw QueryException::semanticalError('DATE_SUB() only supports units of type day and month.'); + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php new file mode 100644 index 0000000..b9d36d1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\AST\Node; + +/** + * Abtract Function Node. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +abstract class FunctionNode extends Node +{ + public $name; + + public function __construct($name) + { + $this->name = $name; + } + + abstract public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker); + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkFunction($this); + } + + abstract public function parse(\Doctrine\ORM\Query\Parser $parser); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php new file mode 100644 index 0000000..1b75929 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "IDENTITY" "(" SingleValuedAssociationPathExpression ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.2 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class IdentityFunction extends FunctionNode +{ + public $pathExpression; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + $dqlAlias = $this->pathExpression->identificationVariable; + $assocField = $this->pathExpression->field; + + $qComp = $sqlWalker->getQueryComponent($dqlAlias); + $class = $qComp['metadata']; + $assoc = $class->associationMappings[$assocField]; + + $tableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); + + return $tableAlias . '.' . reset($assoc['targetToSourceKeyColumns']);; + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->pathExpression = $parser->SingleValuedAssociationPathExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php new file mode 100644 index 0000000..82dd4b4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "LENGTH" "(" StringPrimary ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LengthFunction extends FunctionNode +{ + public $stringPrimary; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getLengthExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LocateFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LocateFunction.php new file mode 100644 index 0000000..e630b2e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LocateFunction.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LocateFunction extends FunctionNode +{ + public $firstStringPrimary; + public $secondStringPrimary; + public $simpleArithmeticExpression = false; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + + return $sqlWalker->getConnection()->getDatabasePlatform()->getLocateExpression( + $sqlWalker->walkStringPrimary($this->secondStringPrimary), // its the other way around in platform + $sqlWalker->walkStringPrimary($this->firstStringPrimary), + (($this->simpleArithmeticExpression) + ? $sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression) + : false + ) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstStringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_COMMA); + + $this->secondStringPrimary = $parser->StringPrimary(); + + $lexer = $parser->getLexer(); + if ($lexer->isNextToken(Lexer::T_COMMA)) { + $parser->match(Lexer::T_COMMA); + + $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + } + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php new file mode 100644 index 0000000..7bc092d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "LOWER" "(" StringPrimary ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LowerFunction extends FunctionNode +{ + public $stringPrimary; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getLowerExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php new file mode 100644 index 0000000..53f064a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class ModFunction extends FunctionNode +{ + public $firstSimpleArithmeticExpression; + public $secondSimpleArithmeticExpression; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getModExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression), + $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_MOD); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_COMMA); + + $this->secondSimpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php new file mode 100644 index 0000000..3decb91 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php @@ -0,0 +1,122 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "SIZE" "(" CollectionValuedPathExpression ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class SizeFunction extends FunctionNode +{ + public $collectionPathExpression; + + /** + * @override + * @todo If the collection being counted is already joined, the SQL can be simpler (more efficient). + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + $dqlAlias = $this->collectionPathExpression->identificationVariable; + $assocField = $this->collectionPathExpression->field; + + $qComp = $sqlWalker->getQueryComponent($dqlAlias); + $class = $qComp['metadata']; + $assoc = $class->associationMappings[$assocField]; + $sql = 'SELECT COUNT(*) FROM '; + + if ($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_MANY) { + $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']); + $targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->getTableName()); + $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); + + $sql .= $targetClass->getQuotedTableName($platform) . ' ' . $targetTableAlias . ' WHERE '; + + $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']]; + + $first = true; + + foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) { + if ($first) $first = false; else $sql .= ' AND '; + + $sql .= $targetTableAlias . '.' . $sourceColumn + . ' = ' + . $sourceTableAlias . '.' . $class->getQuotedColumnName($class->fieldNames[$targetColumn], $platform); + } + } else { // many-to-many + $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']); + + $owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']]; + $joinTable = $owningAssoc['joinTable']; + + // SQL table aliases + $joinTableAlias = $sqlWalker->getSQLTableAlias($joinTable['name']); + $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); + + // join to target table + $sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $platform) . ' ' . $joinTableAlias . ' WHERE '; + + $joinColumns = $assoc['isOwningSide'] + ? $joinTable['joinColumns'] + : $joinTable['inverseJoinColumns']; + + $first = true; + + foreach ($joinColumns as $joinColumn) { + if ($first) $first = false; else $sql .= ' AND '; + + $sourceColumnName = $class->getQuotedColumnName( + $class->fieldNames[$joinColumn['referencedColumnName']], $platform + ); + + $sql .= $joinTableAlias . '.' . $joinColumn['name'] + . ' = ' + . $sourceTableAlias . '.' . $sourceColumnName; + } + } + + return '(' . $sql . ')'; + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $lexer = $parser->getLexer(); + + $parser->match(Lexer::T_SIZE); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->collectionPathExpression = $parser->CollectionValuedPathExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SqrtFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SqrtFunction.php new file mode 100644 index 0000000..087b93b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SqrtFunction.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "SQRT" "(" SimpleArithmeticExpression ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class SqrtFunction extends FunctionNode +{ + public $simpleArithmeticExpression; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + //TODO: Use platform to get SQL + return 'SQRT(' . $sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression) . ')'; + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php new file mode 100644 index 0000000..c0e6223 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class SubstringFunction extends FunctionNode +{ + public $stringPrimary; + public $firstSimpleArithmeticExpression; + public $secondSimpleArithmeticExpression = null; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $optionalSecondSimpleArithmeticExpression = null; + if ($this->secondSimpleArithmeticExpression !== null) { + $optionalSecondSimpleArithmeticExpression = $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression); + } + + return $sqlWalker->getConnection()->getDatabasePlatform()->getSubstringExpression( + $sqlWalker->walkStringPrimary($this->stringPrimary), + $sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression), + $optionalSecondSimpleArithmeticExpression + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_COMMA); + + $this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $lexer = $parser->getLexer(); + if ($lexer->isNextToken(Lexer::T_COMMA)) { + $parser->match(Lexer::T_COMMA); + + $this->secondSimpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + } + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php new file mode 100644 index 0000000..47b3cec --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php @@ -0,0 +1,100 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class TrimFunction extends FunctionNode +{ + public $leading; + public $trailing; + public $both; + public $trimChar = false; + public $stringPrimary; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $pos = AbstractPlatform::TRIM_UNSPECIFIED; + if ($this->leading) { + $pos = AbstractPlatform::TRIM_LEADING; + } else if ($this->trailing) { + $pos = AbstractPlatform::TRIM_TRAILING; + } else if ($this->both) { + $pos = AbstractPlatform::TRIM_BOTH; + } + + return $sqlWalker->getConnection()->getDatabasePlatform()->getTrimExpression( + $sqlWalker->walkStringPrimary($this->stringPrimary), + $pos, + ($this->trimChar != false) ? $sqlWalker->getConnection()->quote($this->trimChar) : false + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $lexer = $parser->getLexer(); + + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + if (strcasecmp('leading', $lexer->lookahead['value']) === 0) { + $parser->match(Lexer::T_LEADING); + $this->leading = true; + } else if (strcasecmp('trailing', $lexer->lookahead['value']) === 0) { + $parser->match(Lexer::T_TRAILING); + $this->trailing = true; + } else if (strcasecmp('both', $lexer->lookahead['value']) === 0) { + $parser->match(Lexer::T_BOTH); + $this->both = true; + } + + if ($lexer->isNextToken(Lexer::T_STRING)) { + $parser->match(Lexer::T_STRING); + $this->trimChar = $lexer->token['value']; + } + + if ($this->leading || $this->trailing || $this->both || $this->trimChar) { + $parser->match(Lexer::T_FROM); + } + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php new file mode 100644 index 0000000..16a0ed8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "UPPER" "(" StringPrimary ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class UpperFunction extends FunctionNode +{ + public $stringPrimary; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getUpperExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GeneralCaseExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GeneralCaseExpression.php new file mode 100644 index 0000000..facacd5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GeneralCaseExpression.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" + * + * @since 2.2 + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GeneralCaseExpression extends Node +{ + public $whenClauses = array(); + public $elseScalarExpression = null; + + public function __construct(array $whenClauses, $elseScalarExpression) + { + $this->whenClauses = $whenClauses; + $this->elseScalarExpression = $elseScalarExpression; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkGeneralCaseExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GroupByClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GroupByClause.php new file mode 100644 index 0000000..d5ae72f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GroupByClause.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of GroupByClause + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GroupByClause extends Node +{ + public $groupByItems = array(); + + public function __construct(array $groupByItems) + { + $this->groupByItems = $groupByItems; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkGroupByClause($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/HavingClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/HavingClause.php new file mode 100644 index 0000000..08912b0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/HavingClause.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of HavingClause + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class HavingClause extends Node +{ + public $conditionalExpression; + + public function __construct($conditionalExpression) + { + $this->conditionalExpression = $conditionalExpression; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkHavingClause($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IdentificationVariableDeclaration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IdentificationVariableDeclaration.php new file mode 100644 index 0000000..2f590cc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IdentificationVariableDeclaration.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}* + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class IdentificationVariableDeclaration extends Node +{ + public $rangeVariableDeclaration = null; + public $indexBy = null; + public $joinVariableDeclarations = array(); + + public function __construct($rangeVariableDecl, $indexBy, array $joinVariableDecls) + { + $this->rangeVariableDeclaration = $rangeVariableDecl; + $this->indexBy = $indexBy; + $this->joinVariableDeclarations = $joinVariableDecls; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkIdentificationVariableDeclaration($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InExpression.php new file mode 100644 index 0000000..15c517d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InExpression.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * InExpression ::= StateFieldPathExpression ["NOT"] "IN" "(" (Literal {"," Literal}* | Subselect) ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class InExpression extends Node +{ + public $not; + public $expression; + public $literals = array(); + public $subselect; + + public function __construct($expression) + { + $this->expression = $expression; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkInExpression($this); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IndexBy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IndexBy.php new file mode 100644 index 0000000..16f2206 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IndexBy.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * IndexBy ::= "INDEX" "BY" SimpleStateFieldPathExpression + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class IndexBy extends Node +{ + public $simpleStateFieldPathExpression = null; + + public function __construct($simpleStateFieldPathExpression) + { + $this->simpleStateFieldPathExpression = $simpleStateFieldPathExpression; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkIndexBy($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InputParameter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InputParameter.php new file mode 100644 index 0000000..cf04d70 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InputParameter.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of InputParameter + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class InputParameter extends Node +{ + public $isNamed; + public $name; + + /** + * @param string $value + */ + public function __construct($value) + { + if (strlen($value) == 1) { + throw \Doctrine\ORM\Query\QueryException::invalidParameterFormat($value); + } + + $param = substr($value, 1); + $this->isNamed = ! is_numeric($param); + $this->name = $param; + } + + public function dispatch($walker) + { + return $walker->walkInputParameter($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InstanceOfExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InstanceOfExpression.php new file mode 100644 index 0000000..4f254b5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InstanceOfExpression.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")") + * InstanceOfParameter ::= AbstractSchemaName | InputParameter + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class InstanceOfExpression extends Node +{ + public $not; + public $identificationVariable; + public $value; + + public function __construct($identVariable) + { + $this->identificationVariable = $identVariable; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkInstanceOfExpression($this); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Join.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Join.php new file mode 100644 index 0000000..084f7d7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Join.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinAssociationPathExpression + * ["AS"] AliasIdentificationVariable [("ON" | "WITH") ConditionalExpression] + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Join extends Node +{ + const JOIN_TYPE_LEFT = 1; + const JOIN_TYPE_LEFTOUTER = 2; + const JOIN_TYPE_INNER = 3; + + public $joinType = self::JOIN_TYPE_INNER; + public $joinAssociationPathExpression = null; + public $aliasIdentificationVariable = null; + public $conditionalExpression = null; + + public function __construct($joinType, $joinAssocPathExpr, $aliasIdentVar) + { + $this->joinType = $joinType; + $this->joinAssociationPathExpression = $joinAssocPathExpr; + $this->aliasIdentificationVariable = $aliasIdentVar; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkJoin($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationPathExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationPathExpression.php new file mode 100644 index 0000000..f9300b2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationPathExpression.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinAssociationPathExpression ::= IdentificationVariable "." (SingleValuedAssociationField | CollectionValuedAssociationField) + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class JoinAssociationPathExpression extends Node +{ + public $identificationVariable; + public $associationField; + + public function __construct($identificationVariable, $associationField) + { + $this->identificationVariable = $identificationVariable; + $this->associationField = $associationField; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkJoinPathExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinVariableDeclaration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinVariableDeclaration.php new file mode 100644 index 0000000..7fa9562 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinVariableDeclaration.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinVariableDeclaration ::= Join [IndexBy] + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class JoinVariableDeclaration extends Node +{ + public $join = null; + public $indexBy = null; + + public function __construct($join, $indexBy) + { + $this->join = $join; + $this->indexBy = $indexBy; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkJoinVariableDeclaration($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/LikeExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/LikeExpression.php new file mode 100644 index 0000000..7985be4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/LikeExpression.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * LikeExpression ::= StringExpression ["NOT"] "LIKE" string ["ESCAPE" char] + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class LikeExpression extends Node +{ + public $not; + public $stringExpression; + public $stringPattern; + public $escapeChar; + + public function __construct($stringExpression, $stringPattern, $escapeChar = null) + { + $this->stringExpression = $stringExpression; + $this->stringPattern = $stringPattern; + $this->escapeChar = $escapeChar; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkLikeExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Literal.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Literal.php new file mode 100644 index 0000000..426907f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Literal.php @@ -0,0 +1,24 @@ +type = $type; + $this->value = $value; + } + + public function dispatch($walker) + { + return $walker->walkLiteral($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Node.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Node.php new file mode 100644 index 0000000..8ef13c0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Node.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Abstract class of an AST node + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class Node +{ + /** + * Double-dispatch method, supposed to dispatch back to the walker. + * + * Implementation is not mandatory for all nodes. + * + * @param $walker + */ + public function dispatch($walker) + { + throw ASTException::noDispatchForNode($this); + } + + /** + * Dumps the AST Node into a string representation for information purpose only + * + * @return string + */ + public function __toString() + { + return $this->dump($this); + } + + public function dump($obj) + { + static $ident = 0; + + $str = ''; + + if ($obj instanceof Node) { + $str .= get_class($obj) . '(' . PHP_EOL; + $props = get_object_vars($obj); + + foreach ($props as $name => $prop) { + $ident += 4; + $str .= str_repeat(' ', $ident) . '"' . $name . '": ' + . $this->dump($prop) . ',' . PHP_EOL; + $ident -= 4; + } + + $str .= str_repeat(' ', $ident) . ')'; + } else if (is_array($obj)) { + $ident += 4; + $str .= 'array('; + $some = false; + + foreach ($obj as $k => $v) { + $str .= PHP_EOL . str_repeat(' ', $ident) . '"' + . $k . '" => ' . $this->dump($v) . ','; + $some = true; + } + + $ident -= 4; + $str .= ($some ? PHP_EOL . str_repeat(' ', $ident) : '') . ')'; + } else if (is_object($obj)) { + $str .= 'instanceof(' . get_class($obj) . ')'; + } else { + $str .= var_export($obj, true); + } + + return $str; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullComparisonExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullComparisonExpression.php new file mode 100644 index 0000000..0e64bd2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullComparisonExpression.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class NullComparisonExpression extends Node +{ + public $not; + public $expression; + + public function __construct($expression) + { + $this->expression = $expression; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkNullComparisonExpression($this); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullIfExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullIfExpression.php new file mode 100644 index 0000000..12c8c14 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullIfExpression.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" + * + * @since 2.1 + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class NullIfExpression extends Node +{ + public $firstExpression; + + public $secondExpression; + + public function __construct($firstExpression, $secondExpression) + { + $this->firstExpression = $firstExpression; + $this->secondExpression = $secondExpression; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkNullIfExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByClause.php new file mode 100644 index 0000000..7e23059 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByClause.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}* + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class OrderByClause extends Node +{ + public $orderByItems = array(); + + public function __construct(array $orderByItems) + { + $this->orderByItems = $orderByItems; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkOrderByClause($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByItem.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByItem.php new file mode 100644 index 0000000..207cbd3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByItem.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * OrderByItem ::= (ResultVariable | StateFieldPathExpression) ["ASC" | "DESC"] + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class OrderByItem extends Node +{ + public $expression; + public $type; + + public function __construct($expression) + { + $this->expression = $expression; + } + + public function isAsc() + { + return strtoupper($this->type) == 'ASC'; + } + + public function isDesc() + { + return strtoupper($this->type) == 'DESC'; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkOrderByItem($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php new file mode 100644 index 0000000..d1757d7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php @@ -0,0 +1,15 @@ +identificationVariable = $identificationVariable; + $this->partialFieldSet = $partialFieldSet; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PathExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PathExpression.php new file mode 100644 index 0000000..2f78b9c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PathExpression.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression + * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression + * StateFieldPathExpression ::= SimpleStateFieldPathExpression | SimpleStateFieldAssociationPathExpression + * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField + * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField + * StateField ::= {EmbeddedClassStateField "."}* SimpleStateField + * SimpleStateFieldPathExpression ::= IdentificationVariable "." StateField + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class PathExpression extends Node +{ + const TYPE_COLLECTION_VALUED_ASSOCIATION = 2; + const TYPE_SINGLE_VALUED_ASSOCIATION = 4; + const TYPE_STATE_FIELD = 8; + + public $type; + public $expectedType; + public $identificationVariable; + public $field; + + public function __construct($expectedType, $identificationVariable, $field = null) + { + $this->expectedType = $expectedType; + $this->identificationVariable = $identificationVariable; + $this->field = $field; + } + + public function dispatch($walker) + { + return $walker->walkPathExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php new file mode 100644 index 0000000..11b60ed --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class QuantifiedExpression extends Node +{ + public $type; + public $subselect; + + public function __construct($subselect) + { + $this->subselect = $subselect; + } + + public function isAll() + { + return strtoupper($this->type) == 'ALL'; + } + + public function isAny() + { + return strtoupper($this->type) == 'ANY'; + } + + public function isSome() + { + return strtoupper($this->type) == 'SOME'; + } + + /** + * @override + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkQuantifiedExpression($this); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php new file mode 100644 index 0000000..cf8b180 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class RangeVariableDeclaration extends Node +{ + public $abstractSchemaName; + public $aliasIdentificationVariable; + + public function __construct($abstractSchemaName, $aliasIdentificationVar) + { + $this->abstractSchemaName = $abstractSchemaName; + $this->aliasIdentificationVariable = $aliasIdentificationVar; + } + + public function dispatch($walker) + { + return $walker->walkRangeVariableDeclaration($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectClause.php new file mode 100644 index 0000000..cf8e9df --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectClause.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SelectClause = "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression} + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SelectClause extends Node +{ + public $isDistinct; + public $selectExpressions = array(); + + public function __construct(array $selectExpressions, $isDistinct) + { + $this->isDistinct = $isDistinct; + $this->selectExpressions = $selectExpressions; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSelectClause($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectExpression.php new file mode 100644 index 0000000..fd0d49b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectExpression.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SelectExpression ::= IdentificationVariable ["." "*"] | StateFieldPathExpression | + * (AggregateExpression | "(" Subselect ")") [["AS"] ["HIDDEN"] FieldAliasIdentificationVariable] + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SelectExpression extends Node +{ + public $expression; + public $fieldIdentificationVariable; + public $hiddenAliasResultVariable; + + public function __construct($expression, $fieldIdentificationVariable, $hiddenAliasResultVariable = false) + { + $this->expression = $expression; + $this->fieldIdentificationVariable = $fieldIdentificationVariable; + $this->hiddenAliasResultVariable = $hiddenAliasResultVariable; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSelectExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectStatement.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectStatement.php new file mode 100644 index 0000000..d65a97b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectStatement.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SelectStatement = SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SelectStatement extends Node +{ + public $selectClause; + public $fromClause; + public $whereClause; + public $groupByClause; + public $havingClause; + public $orderByClause; + + public function __construct($selectClause, $fromClause) { + $this->selectClause = $selectClause; + $this->fromClause = $fromClause; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSelectStatement($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleArithmeticExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleArithmeticExpression.php new file mode 100644 index 0000000..3fb6f05 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleArithmeticExpression.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}* + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleArithmeticExpression extends Node +{ + public $arithmeticTerms = array(); + + public function __construct(array $arithmeticTerms) + { + $this->arithmeticTerms = $arithmeticTerms; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleArithmeticExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleCaseExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleCaseExpression.php new file mode 100644 index 0000000..586928d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleCaseExpression.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" + * + * @since 2.2 + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleCaseExpression extends Node +{ + public $caseOperand = null; + public $simpleWhenClauses = array(); + public $elseScalarExpression = null; + + public function __construct($caseOperand, array $simpleWhenClauses, $elseScalarExpression) + { + $this->caseOperand = $caseOperand; + $this->simpleWhenClauses = $simpleWhenClauses; + $this->elseScalarExpression = $elseScalarExpression; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleCaseExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php new file mode 100644 index 0000000..8a40280 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleSelectClause extends Node +{ + public $isDistinct = false; + public $simpleSelectExpression; + + public function __construct($simpleSelectExpression, $isDistinct) + { + $this->simpleSelectExpression = $simpleSelectExpression; + $this->isDistinct = $isDistinct; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleSelectClause($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectExpression.php new file mode 100644 index 0000000..1648c4d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectExpression.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleSelectExpression ::= StateFieldPathExpression | IdentificationVariable + * | (AggregateExpression [["AS"] FieldAliasIdentificationVariable]) + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleSelectExpression extends Node +{ + public $expression; + public $fieldIdentificationVariable; + + public function __construct($expression) + { + $this->expression = $expression; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleSelectExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleWhenClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleWhenClause.php new file mode 100644 index 0000000..0345328 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleWhenClause.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression + * + * @since 2.2 + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleWhenClause extends Node +{ + public $caseScalarExpression = null; + public $thenScalarExpression = null; + + public function __construct($caseScalarExpression, $thenScalarExpression) + { + $this->caseScalarExpression = $caseScalarExpression; + $this->thenScalarExpression = $thenScalarExpression; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkWhenClauseExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Subselect.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Subselect.php new file mode 100644 index 0000000..548e42b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Subselect.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Subselect extends Node +{ + public $simpleSelectClause; + public $subselectFromClause; + public $whereClause; + public $groupByClause; + public $havingClause; + public $orderByClause; + + public function __construct($simpleSelectClause, $subselectFromClause) + { + $this->simpleSelectClause = $simpleSelectClause; + $this->subselectFromClause = $subselectFromClause; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSubselect($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php new file mode 100644 index 0000000..c7c2f35 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SubselectFromClause extends Node +{ + public $identificationVariableDeclarations = array(); + + public function __construct(array $identificationVariableDeclarations) + { + $this->identificationVariableDeclarations = $identificationVariableDeclarations; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSubselectFromClause($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateClause.php new file mode 100644 index 0000000..158cf24 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateClause.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * UpdateClause ::= "UPDATE" AbstractSchemaName [["AS"] AliasIdentificationVariable] "SET" UpdateItem {"," UpdateItem}* + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class UpdateClause extends Node +{ + public $abstractSchemaName; + public $aliasIdentificationVariable; + public $updateItems = array(); + + public function __construct($abstractSchemaName, array $updateItems) + { + $this->abstractSchemaName = $abstractSchemaName; + $this->updateItems = $updateItems; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkUpdateClause($this); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateItem.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateItem.php new file mode 100644 index 0000000..5f8b678 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateItem.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * UpdateItem ::= [IdentificationVariable "."] {StateField | SingleValuedAssociationField} "=" NewValue + * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary | + * EnumPrimary | SimpleEntityExpression | "NULL" + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class UpdateItem extends Node +{ + public $pathExpression; + public $newValue; + + public function __construct($pathExpression, $newValue) + { + $this->pathExpression = $pathExpression; + $this->newValue = $newValue; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkUpdateItem($this); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateStatement.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateStatement.php new file mode 100644 index 0000000..d1e152e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateStatement.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * UpdateStatement = UpdateClause [WhereClause] + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class UpdateStatement extends Node +{ + public $updateClause; + public $whereClause; + + public function __construct($updateClause) + { + $this->updateClause = $updateClause; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkUpdateStatement($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhenClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhenClause.php new file mode 100644 index 0000000..69556e5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhenClause.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression + * + * @since 2.2 + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class WhenClause extends Node +{ + public $caseConditionExpression = null; + public $thenScalarExpression = null; + + public function __construct($caseConditionExpression, $thenScalarExpression) + { + $this->caseConditionExpression = $caseConditionExpression; + $this->thenScalarExpression = $thenScalarExpression; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkWhenClauseExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhereClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhereClause.php new file mode 100644 index 0000000..5d3f9c9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhereClause.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * WhereClause ::= "WHERE" ConditionalExpression + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class WhereClause extends Node +{ + public $conditionalExpression; + + public function __construct($conditionalExpression) + { + $this->conditionalExpression = $conditionalExpression; + } + + public function dispatch($sqlWalker) + { + return $sqlWalker->walkWhereClause($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php new file mode 100644 index 0000000..d639223 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Cache\QueryCacheProfile; + +/** + * Base class for SQL statement executors. + * + * @author Roman Borschel + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://www.doctrine-project.org + * @since 2.0 + * @todo Rename: AbstractSQLExecutor + */ +abstract class AbstractSqlExecutor +{ + /** + * @var array + */ + protected $_sqlStatements; + + /** + * @var QueryCacheProfile + */ + protected $queryCacheProfile; + + /** + * Gets the SQL statements that are executed by the executor. + * + * @return array All the SQL update statements. + */ + public function getSqlStatements() + { + return $this->_sqlStatements; + } + + public function setQueryCacheProfile(QueryCacheProfile $qcp) + { + $this->queryCacheProfile = $qcp; + } + + /** + * Executes all sql statements. + * + * @param \Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries. + * @param array $params The parameters. + * @param array $types The parameter types. + * @return \Doctrine\DBAL\Driver\Statement + */ + abstract public function execute(Connection $conn, array $params, array $types); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php new file mode 100644 index 0000000..51f4b8c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection, + Doctrine\ORM\Query\AST; + +/** + * Executes the SQL statements for bulk DQL DELETE statements on classes in + * Class Table Inheritance (JOINED). + * + * @author Roman Borschel + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://www.doctrine-project.org + * @since 2.0 + */ +class MultiTableDeleteExecutor extends AbstractSqlExecutor +{ + private $_createTempTableSql; + private $_dropTempTableSql; + private $_insertSql; + + /** + * Initializes a new MultiTableDeleteExecutor. + * + * @param Node $AST The root AST node of the DQL query. + * @param SqlWalker $sqlWalker The walker used for SQL generation from the AST. + * @internal Any SQL construction and preparation takes place in the constructor for + * best performance. With a query cache the executor will be cached. + */ + public function __construct(AST\Node $AST, $sqlWalker) + { + $em = $sqlWalker->getEntityManager(); + $conn = $em->getConnection(); + $platform = $conn->getDatabasePlatform(); + + $primaryClass = $em->getClassMetadata($AST->deleteClause->abstractSchemaName); + $primaryDqlAlias = $AST->deleteClause->aliasIdentificationVariable; + $rootClass = $em->getClassMetadata($primaryClass->rootEntityName); + + $tempTable = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName()); + $idColumnNames = $rootClass->getIdentifierColumnNames(); + $idColumnList = implode(', ', $idColumnNames); + + // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() + $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $primaryDqlAlias); + + $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' + . ' SELECT t0.' . implode(', t0.', $idColumnNames); + + $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $primaryDqlAlias); + $fromClause = new AST\FromClause(array(new AST\IdentificationVariableDeclaration($rangeDecl, null, array()))); + $this->_insertSql .= $sqlWalker->walkFromClause($fromClause); + + // Append WHERE clause, if there is one. + if ($AST->whereClause) { + $this->_insertSql .= $sqlWalker->walkWhereClause($AST->whereClause); + } + + // 2. Create ID subselect statement used in DELETE ... WHERE ... IN (subselect) + $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable; + + // 3. Create and store DELETE statements + $classNames = array_merge($primaryClass->parentClasses, array($primaryClass->name), $primaryClass->subClasses); + foreach (array_reverse($classNames) as $className) { + $tableName = $em->getClassMetadata($className)->getQuotedTableName($platform); + $this->_sqlStatements[] = 'DELETE FROM ' . $tableName + . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')'; + } + + // 4. Store DDL for temporary identifier table. + $columnDefinitions = array(); + foreach ($idColumnNames as $idColumnName) { + $columnDefinitions[$idColumnName] = array( + 'notnull' => true, + 'type' => \Doctrine\DBAL\Types\Type::getType($rootClass->getTypeOfColumn($idColumnName)) + ); + } + $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' + . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; + $this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable); + } + + /** + * {@inheritDoc} + */ + public function execute(Connection $conn, array $params, array $types) + { + $numDeleted = 0; + + // Create temporary id table + $conn->executeUpdate($this->_createTempTableSql); + + // Insert identifiers + $numDeleted = $conn->executeUpdate($this->_insertSql, $params, $types); + + // Execute DELETE statements + foreach ($this->_sqlStatements as $sql) { + $conn->executeUpdate($sql); + } + + // Drop temporary table + $conn->executeUpdate($this->_dropTempTableSql); + + return $numDeleted; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php new file mode 100644 index 0000000..8be24e5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php @@ -0,0 +1,178 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection, + Doctrine\DBAL\Types\Type, + Doctrine\ORM\Query\AST; + +/** + * Executes the SQL statements for bulk DQL UPDATE statements on classes in + * Class Table Inheritance (JOINED). + * + * @author Roman Borschel + * @since 2.0 + */ +class MultiTableUpdateExecutor extends AbstractSqlExecutor +{ + private $_createTempTableSql; + private $_dropTempTableSql; + private $_insertSql; + private $_sqlParameters = array(); + private $_numParametersInUpdateClause = 0; + + /** + * Initializes a new MultiTableUpdateExecutor. + * + * @param Node $AST The root AST node of the DQL query. + * @param SqlWalker $sqlWalker The walker used for SQL generation from the AST. + * @internal Any SQL construction and preparation takes place in the constructor for + * best performance. With a query cache the executor will be cached. + */ + public function __construct(AST\Node $AST, $sqlWalker) + { + $em = $sqlWalker->getEntityManager(); + $conn = $em->getConnection(); + $platform = $conn->getDatabasePlatform(); + + $updateClause = $AST->updateClause; + + $primaryClass = $sqlWalker->getEntityManager()->getClassMetadata($updateClause->abstractSchemaName); + $rootClass = $em->getClassMetadata($primaryClass->rootEntityName); + + $updateItems = $updateClause->updateItems; + + $tempTable = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName()); + $idColumnNames = $rootClass->getIdentifierColumnNames(); + $idColumnList = implode(', ', $idColumnNames); + + // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() + $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $updateClause->aliasIdentificationVariable); + + $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' + . ' SELECT t0.' . implode(', t0.', $idColumnNames); + + $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $updateClause->aliasIdentificationVariable); + $fromClause = new AST\FromClause(array(new AST\IdentificationVariableDeclaration($rangeDecl, null, array()))); + + $this->_insertSql .= $sqlWalker->walkFromClause($fromClause); + + // 2. Create ID subselect statement used in UPDATE ... WHERE ... IN (subselect) + $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable; + + // 3. Create and store UPDATE statements + $classNames = array_merge($primaryClass->parentClasses, array($primaryClass->name), $primaryClass->subClasses); + $i = -1; + + foreach (array_reverse($classNames) as $className) { + $affected = false; + $class = $em->getClassMetadata($className); + $updateSql = 'UPDATE ' . $class->getQuotedTableName($platform) . ' SET '; + + foreach ($updateItems as $updateItem) { + $field = $updateItem->pathExpression->field; + + if (isset($class->fieldMappings[$field]) && ! isset($class->fieldMappings[$field]['inherited']) || + isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]['inherited'])) { + $newValue = $updateItem->newValue; + + if ( ! $affected) { + $affected = true; + ++$i; + } else { + $updateSql .= ', '; + } + + $updateSql .= $sqlWalker->walkUpdateItem($updateItem); + + //FIXME: parameters can be more deeply nested. traverse the tree. + //FIXME (URGENT): With query cache the parameter is out of date. Move to execute() stage. + if ($newValue instanceof AST\InputParameter) { + $paramKey = $newValue->name; + $this->_sqlParameters[$i]['parameters'][] = $sqlWalker->getQuery()->getParameter($paramKey); + $this->_sqlParameters[$i]['types'][] = $sqlWalker->getQuery()->getParameterType($paramKey); + + ++$this->_numParametersInUpdateClause; + } + } + } + + if ($affected) { + $this->_sqlStatements[$i] = $updateSql . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')'; + } + } + + // Append WHERE clause to insertSql, if there is one. + if ($AST->whereClause) { + $this->_insertSql .= $sqlWalker->walkWhereClause($AST->whereClause); + } + + // 4. Store DDL for temporary identifier table. + $columnDefinitions = array(); + + foreach ($idColumnNames as $idColumnName) { + $columnDefinitions[$idColumnName] = array( + 'notnull' => true, + 'type' => Type::getType($rootClass->getTypeOfColumn($idColumnName)) + ); + } + + $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' + . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; + + $this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable); + } + + /** + * {@inheritDoc} + */ + public function execute(Connection $conn, array $params, array $types) + { + $numUpdated = 0; + + // Create temporary id table + $conn->executeUpdate($this->_createTempTableSql); + + // Insert identifiers. Parameters from the update clause are cut off. + $numUpdated = $conn->executeUpdate( + $this->_insertSql, + array_slice($params, $this->_numParametersInUpdateClause), + array_slice($types, $this->_numParametersInUpdateClause) + ); + + // Execute UPDATE statements + for ($i=0, $count=count($this->_sqlStatements); $i<$count; ++$i) { + $parameters = array(); + $types = array(); + + if (isset($this->_sqlParameters[$i])) { + $parameters = isset($this->_sqlParameters[$i]['parameters']) ? $this->_sqlParameters[$i]['parameters'] : array(); + $types = isset($this->_sqlParameters[$i]['types']) ? $this->_sqlParameters[$i]['types'] : array(); + } + + $conn->executeUpdate($this->_sqlStatements[$i], $parameters, $types); + } + + // Drop temporary table + $conn->executeUpdate($this->_dropTempTableSql); + + return $numUpdated; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php new file mode 100644 index 0000000..61c42d8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection, + Doctrine\ORM\Query\AST\SelectStatement, + Doctrine\ORM\Query\SqlWalker; + +/** + * Executor that executes the SQL statement for simple DQL SELECT statements. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Roman Borschel + * @link www.doctrine-project.org + * @since 2.0 + */ +class SingleSelectExecutor extends AbstractSqlExecutor +{ + public function __construct(SelectStatement $AST, SqlWalker $sqlWalker) + { + $this->_sqlStatements = $sqlWalker->walkSelectStatement($AST); + } + + /** + * {@inheritDoc} + */ + public function execute(Connection $conn, array $params, array $types) + { + return $conn->executeQuery($this->_sqlStatements, $params, $types, $this->queryCacheProfile); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php new file mode 100644 index 0000000..467ce54 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection, + Doctrine\ORM\Query\AST; + +/** + * Executor that executes the SQL statements for DQL DELETE/UPDATE statements on classes + * that are mapped to a single table. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @author Roman Borschel + * @link www.doctrine-project.org + * @since 2.0 + * @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor. + */ +class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor +{ + public function __construct(AST\Node $AST, $sqlWalker) + { + if ($AST instanceof AST\UpdateStatement) { + $this->_sqlStatements = $sqlWalker->walkUpdateStatement($AST); + } else if ($AST instanceof AST\DeleteStatement) { + $this->_sqlStatements = $sqlWalker->walkDeleteStatement($AST); + } + } + + /** + * {@inheritDoc} + */ + public function execute(Connection $conn, array $params, array $types) + { + return $conn->executeUpdate($this->_sqlStatements, $params, $types); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr.php new file mode 100644 index 0000000..adcd25c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr.php @@ -0,0 +1,595 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * This class is used to generate DQL expressions via a set of PHP static functions + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + * @todo Rename: ExpressionBuilder + */ +class Expr +{ + /** + * Creates a conjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?1) AND (u.role = ?2) + * $expr->andX($expr->eq('u.type', ':1'), $expr->eq('u.role', ':2')); + * + * @param \Doctrine\ORM\Query\Expr\Comparison | + * \Doctrine\ORM\Query\Expr\Func | + * \Doctrine\ORM\Query\Expr\Orx + * $x Optional clause. Defaults = null, but requires at least one defined when converting to string. + * @return Expr\Andx + */ + public function andX($x = null) + { + return new Expr\Andx(func_get_args()); + } + + /** + * Creates a disjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?1) OR (u.role = ?2) + * $q->where($q->expr()->orX('u.type = ?1', 'u.role = ?2')); + * + * @param mixed $x Optional clause. Defaults = null, but requires + * at least one defined when converting to string. + * @return Expr\Orx + */ + public function orX($x = null) + { + return new Expr\Orx(func_get_args()); + } + + /** + * Creates an ASCending order expression. + * + * @param $sort + * @return Expr\OrderBy + */ + public function asc($expr) + { + return new Expr\OrderBy($expr, 'ASC'); + } + + /** + * Creates a DESCending order expression. + * + * @param $sort + * @return Expr\OrderBy + */ + public function desc($expr) + { + return new Expr\OrderBy($expr, 'DESC'); + } + + /** + * Creates an equality comparison expression with the given arguments. + * + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a = . Example: + * + * [php] + * // u.id = ?1 + * $expr->eq('u.id', '?1'); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return Expr\Comparison + */ + public function eq($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::EQ, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <> . Example: + * + * [php] + * // u.id <> ?1 + * $q->where($q->expr()->neq('u.id', '?1')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return Expr\Comparison + */ + public function neq($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::NEQ, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a < . Example: + * + * [php] + * // u.id < ?1 + * $q->where($q->expr()->lt('u.id', '?1')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return Expr\Comparison + */ + public function lt($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::LT, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <= . Example: + * + * [php] + * // u.id <= ?1 + * $q->where($q->expr()->lte('u.id', '?1')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return Expr\Comparison + */ + public function lte($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::LTE, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a > . Example: + * + * [php] + * // u.id > ?1 + * $q->where($q->expr()->gt('u.id', '?1')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return Expr\Comparison + */ + public function gt($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::GT, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a >= . Example: + * + * [php] + * // u.id >= ?1 + * $q->where($q->expr()->gte('u.id', '?1')); + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return Expr\Comparison + */ + public function gte($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::GTE, $y); + } + + /** + * Creates an instance of AVG() function, with the given argument. + * + * @param mixed $x Argument to be used in AVG() function. + * @return Expr\Func + */ + public function avg($x) + { + return new Expr\Func('AVG', array($x)); + } + + /** + * Creates an instance of MAX() function, with the given argument. + * + * @param mixed $x Argument to be used in MAX() function. + * @return Expr\Func + */ + public function max($x) + { + return new Expr\Func('MAX', array($x)); + } + + /** + * Creates an instance of MIN() function, with the given argument. + * + * @param mixed $x Argument to be used in MIN() function. + * @return Expr\Func + */ + public function min($x) + { + return new Expr\Func('MIN', array($x)); + } + + /** + * Creates an instance of COUNT() function, with the given argument. + * + * @param mixed $x Argument to be used in COUNT() function. + * @return Expr\Func + */ + public function count($x) + { + return new Expr\Func('COUNT', array($x)); + } + + /** + * Creates an instance of COUNT(DISTINCT) function, with the given argument. + * + * @param mixed $x Argument to be used in COUNT(DISTINCT) function. + * @return string + */ + public function countDistinct($x) + { + return 'COUNT(DISTINCT ' . implode(', ', func_get_args()) . ')'; + } + + /** + * Creates an instance of EXISTS() function, with the given DQL Subquery. + * + * @param mixed $subquery DQL Subquery to be used in EXISTS() function. + * @return Expr\Func + */ + public function exists($subquery) + { + return new Expr\Func('EXISTS', array($subquery)); + } + + /** + * Creates an instance of ALL() function, with the given DQL Subquery. + * + * @param mixed $subquery DQL Subquery to be used in ALL() function. + * @return Expr\Func + */ + public function all($subquery) + { + return new Expr\Func('ALL', array($subquery)); + } + + /** + * Creates a SOME() function expression with the given DQL subquery. + * + * @param mixed $subquery DQL Subquery to be used in SOME() function. + * @return Expr\Func + */ + public function some($subquery) + { + return new Expr\Func('SOME', array($subquery)); + } + + /** + * Creates an ANY() function expression with the given DQL subquery. + * + * @param mixed $subquery DQL Subquery to be used in ANY() function. + * @return Expr\Func + */ + public function any($subquery) + { + return new Expr\Func('ANY', array($subquery)); + } + + /** + * Creates a negation expression of the given restriction. + * + * @param mixed $restriction Restriction to be used in NOT() function. + * @return Expr\Func + */ + public function not($restriction) + { + return new Expr\Func('NOT', array($restriction)); + } + + /** + * Creates an ABS() function expression with the given argument. + * + * @param mixed $x Argument to be used in ABS() function. + * @return Expr\Func + */ + public function abs($x) + { + return new Expr\Func('ABS', array($x)); + } + + /** + * Creates a product mathematical expression with the given arguments. + * + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a * . Example: + * + * [php] + * // u.salary * u.percentAnualSalaryIncrease + * $q->expr()->prod('u.salary', 'u.percentAnualSalaryIncrease') + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return Expr\Math + */ + public function prod($x, $y) + { + return new Expr\Math($x, '*', $y); + } + + /** + * Creates a difference mathematical expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a - . Example: + * + * [php] + * // u.monthlySubscriptionCount - 1 + * $q->expr()->diff('u.monthlySubscriptionCount', '1') + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return Expr\Math + */ + public function diff($x, $y) + { + return new Expr\Math($x, '-', $y); + } + + /** + * Creates a sum mathematical expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a + . Example: + * + * [php] + * // u.numChildren + 1 + * $q->expr()->diff('u.numChildren', '1') + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return Expr\Math + */ + public function sum($x, $y) + { + return new Expr\Math($x, '+', $y); + } + + /** + * Creates a quotient mathematical expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a / . Example: + * + * [php] + * // u.total / u.period + * $expr->quot('u.total', 'u.period') + * + * @param mixed $x Left expression + * @param mixed $y Right expression + * @return Expr\Math + */ + public function quot($x, $y) + { + return new Expr\Math($x, '/', $y); + } + + /** + * Creates a SQRT() function expression with the given argument. + * + * @param mixed $x Argument to be used in SQRT() function. + * @return Expr\Func + */ + public function sqrt($x) + { + return new Expr\Func('SQRT', array($x)); + } + + /** + * Creates an IN() expression with the given arguments. + * + * @param string $x Field in string format to be restricted by IN() function + * @param mixed $y Argument to be used in IN() function. + * @return Expr\Func + */ + public function in($x, $y) + { + if (is_array($y)) { + foreach ($y as &$literal) { + if ( ! ($literal instanceof Expr\Literal)) { + $literal = $this->_quoteLiteral($literal); + } + } + } + return new Expr\Func($x . ' IN', (array) $y); + } + + /** + * Creates a NOT IN() expression with the given arguments. + * + * @param string $x Field in string format to be restricted by NOT IN() function + * @param mixed $y Argument to be used in NOT IN() function. + * @return Expr\Func + */ + public function notIn($x, $y) + { + if (is_array($y)) { + foreach ($y as &$literal) { + if ( ! ($literal instanceof Expr\Literal)) { + $literal = $this->_quoteLiteral($literal); + } + } + } + return new Expr\Func($x . ' NOT IN', (array) $y); + } + + /** + * Creates an IS NULL expression with the given arguments. + * + * @param string $x Field in string format to be restricted by IS NULL + * @return string + */ + public function isNull($x) + { + return $x . ' IS NULL'; + } + + /** + * Creates an IS NOT NULL expression with the given arguments. + * + * @param string $x Field in string format to be restricted by IS NOT NULL + * @return string + */ + public function isNotNull($x) + { + return $x . ' IS NOT NULL'; + } + + /** + * Creates a LIKE() comparison expression with the given arguments. + * + * @param string $x Field in string format to be inspected by LIKE() comparison. + * @param mixed $y Argument to be used in LIKE() comparison. + * @return Expr\Comparison + */ + public function like($x, $y) + { + return new Expr\Comparison($x, 'LIKE', $y); + } + + /** + * Creates a CONCAT() function expression with the given arguments. + * + * @param mixed $x First argument to be used in CONCAT() function. + * @param mixed $x Second argument to be used in CONCAT() function. + * @return Expr\Func + */ + public function concat($x, $y) + { + return new Expr\Func('CONCAT', array($x, $y)); + } + + /** + * Creates a SUBSTRING() function expression with the given arguments. + * + * @param mixed $x Argument to be used as string to be cropped by SUBSTRING() function. + * @param integer $from Initial offset to start cropping string. May accept negative values. + * @param integer $len Length of crop. May accept negative values. + * @return Expr\Func + */ + public function substring($x, $from, $len = null) + { + $args = array($x, $from); + if (null !== $len) { + $args[] = $len; + } + return new Expr\Func('SUBSTRING', $args); + } + + /** + * Creates a LOWER() function expression with the given argument. + * + * @param mixed $x Argument to be used in LOWER() function. + * @return Expr\Func A LOWER function expression. + */ + public function lower($x) + { + return new Expr\Func('LOWER', array($x)); + } + + /** + * Creates an UPPER() function expression with the given argument. + * + * @param mixed $x Argument to be used in UPPER() function. + * @return Expr\Func An UPPER function expression. + */ + public function upper($x) + { + return new Expr\Func('UPPER', array($x)); + } + + /** + * Creates a LENGTH() function expression with the given argument. + * + * @param mixed $x Argument to be used as argument of LENGTH() function. + * @return Expr\Func A LENGTH function expression. + */ + public function length($x) + { + return new Expr\Func('LENGTH', array($x)); + } + + /** + * Creates a literal expression of the given argument. + * + * @param mixed $literal Argument to be converted to literal. + * @return Expr\Literal + */ + public function literal($literal) + { + return new Expr\Literal($this->_quoteLiteral($literal)); + } + + /** + * Quotes a literal value, if necessary, according to the DQL syntax. + * + * @param mixed $literal The literal value. + * @return string + */ + private function _quoteLiteral($literal) + { + if (is_numeric($literal) && !is_string($literal)) { + return (string) $literal; + } else if (is_bool($literal)) { + return $literal ? "true" : "false"; + } else { + return "'" . str_replace("'", "''", $literal) . "'"; + } + } + + /** + * Creates an instance of BETWEEN() function, with the given argument. + * + * @param mixed $val Valued to be inspected by range values. + * @param integer $x Starting range value to be used in BETWEEN() function. + * @param integer $y End point value to be used in BETWEEN() function. + * @return Expr\Func A BETWEEN expression. + */ + public function between($val, $x, $y) + { + return $val . ' BETWEEN ' . $x . ' AND ' . $y; + } + + /** + * Creates an instance of TRIM() function, with the given argument. + * + * @param mixed $x Argument to be used as argument of TRIM() function. + * @return Expr\Func a TRIM expression. + */ + public function trim($x) + { + return new Expr\Func('TRIM', $x); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Andx.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Andx.php new file mode 100644 index 0000000..c5cf1f3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Andx.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL and parts + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Andx extends Composite +{ + protected $_separator = ' AND '; + protected $_allowedClasses = array( + 'Doctrine\ORM\Query\Expr\Comparison', + 'Doctrine\ORM\Query\Expr\Func', + 'Doctrine\ORM\Query\Expr\Orx', + 'Doctrine\ORM\Query\Expr\Andx', + ); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Base.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Base.php new file mode 100644 index 0000000..975d450 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Base.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Abstract base Expr class for building DQL parts + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class Base +{ + protected $_preSeparator = '('; + protected $_separator = ', '; + protected $_postSeparator = ')'; + protected $_allowedClasses = array(); + + protected $_parts = array(); + + public function __construct($args = array()) + { + $this->addMultiple($args); + } + + public function addMultiple($args = array()) + { + foreach ((array) $args as $arg) { + $this->add($arg); + } + + return $this; + } + + public function add($arg) + { + if ( $arg !== null || ($arg instanceof self && $arg->count() > 0) ) { + // If we decide to keep Expr\Base instances, we can use this check + if ( ! is_string($arg)) { + $class = get_class($arg); + + if ( ! in_array($class, $this->_allowedClasses)) { + throw new \InvalidArgumentException("Expression of type '$class' not allowed in this context."); + } + } + + $this->_parts[] = $arg; + } + + return $this; + } + + public function count() + { + return count($this->_parts); + } + + public function __toString() + { + if ($this->count() == 1) { + return (string) $this->_parts[0]; + } + + return $this->_preSeparator . implode($this->_separator, $this->_parts) . $this->_postSeparator; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Comparison.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Comparison.php new file mode 100644 index 0000000..d42560e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Comparison.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL comparison expressions + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Comparison +{ + const EQ = '='; + const NEQ = '<>'; + const LT = '<'; + const LTE = '<='; + const GT = '>'; + const GTE = '>='; + + private $_leftExpr; + private $_operator; + private $_rightExpr; + + public function __construct($leftExpr, $operator, $rightExpr) + { + $this->_leftExpr = $leftExpr; + $this->_operator = $operator; + $this->_rightExpr = $rightExpr; + } + + public function __toString() + { + return $this->_leftExpr . ' ' . $this->_operator . ' ' . $this->_rightExpr; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Composite.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Composite.php new file mode 100644 index 0000000..036b241 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Composite.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL and parts + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Composite extends Base +{ + public function __toString() + { + if ($this->count() === 1) { + return (string) $this->_parts[0]; + } + + $components = array(); + + foreach ($this->_parts as $part) { + $components[] = $this->processQueryPart($part); + } + + return implode($this->_separator, $components); + } + + + private function processQueryPart($part) + { + $queryPart = (string) $part; + + if (is_object($part) && $part instanceof self && $part->count() > 1) { + return $this->_preSeparator . $queryPart . $this->_postSeparator; + } + + // Fixes DDC-1237: User may have added a where item containing nested expression (with "OR" or "AND") + if (stripos($queryPart, ' OR ') !== false || stripos($queryPart, ' AND ') !== false) { + return $this->_preSeparator . $queryPart . $this->_postSeparator; + } + + return $queryPart; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/From.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/From.php new file mode 100644 index 0000000..e5707cc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/From.php @@ -0,0 +1,88 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL from + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class From +{ + /** + * @var string + */ + private $_from; + + /** + * @var string + */ + private $_alias; + + /** + * @var string + */ + private $_indexBy; + + /** + * @param string $from The class name. + * @param string $alias The alias of the class. + * @param string $indexBy The index for the from. + */ + public function __construct($from, $alias, $indexBy = null) + { + $this->_from = $from; + $this->_alias = $alias; + $this->_indexBy = $indexBy; + } + + /** + * @return string + */ + public function getFrom() + { + return $this->_from; + } + + /** + * @return string + */ + public function getAlias() + { + return $this->_alias; + } + + /** + * @return string + */ + public function __toString() + { + return $this->_from . ' ' . $this->_alias . + ($this->_indexBy ? ' INDEX BY ' . $this->_indexBy : ''); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Func.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Func.php new file mode 100644 index 0000000..48b1a5b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Func.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for generating DQL functions + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Func +{ + private $_name; + private $_arguments; + + public function __construct($name, $arguments) + { + $this->_name = $name; + $this->_arguments = (array) $arguments; + } + + public function __toString() + { + return $this->_name . '(' . implode(', ', $this->_arguments) . ')'; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/GroupBy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/GroupBy.php new file mode 100644 index 0000000..dc36ba3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/GroupBy.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL Group By parts + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GroupBy extends Base +{ + protected $_preSeparator = ''; + protected $_postSeparator = ''; +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Join.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Join.php new file mode 100644 index 0000000..14f5b43 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Join.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL from + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Join +{ + const INNER_JOIN = 'INNER'; + const LEFT_JOIN = 'LEFT'; + + const ON = 'ON'; + const WITH = 'WITH'; + + private $_joinType; + private $_join; + private $_alias; + private $_conditionType; + private $_condition; + private $_indexBy; + + public function __construct($joinType, $join, $alias = null, $conditionType = null, $condition = null, $indexBy = null) + { + $this->_joinType = $joinType; + $this->_join = $join; + $this->_alias = $alias; + $this->_conditionType = $conditionType; + $this->_condition = $condition; + $this->_indexBy = $indexBy; + } + + public function __toString() + { + return strtoupper($this->_joinType) . ' JOIN ' . $this->_join + . ($this->_alias ? ' ' . $this->_alias : '') + . ($this->_condition ? ' ' . strtoupper($this->_conditionType) . ' ' . $this->_condition : '') + . ($this->_indexBy ? ' INDEX BY ' . $this->_indexBy : ''); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Literal.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Literal.php new file mode 100644 index 0000000..c1dd5f7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Literal.php @@ -0,0 +1,9 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL math statements + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Math +{ + private $_leftExpr; + private $_operator; + private $_rightExpr; + + public function __construct($leftExpr, $operator, $rightExpr) + { + $this->_leftExpr = $leftExpr; + $this->_operator = $operator; + $this->_rightExpr = $rightExpr; + } + + public function __toString() + { + // Adjusting Left Expression + $leftExpr = (string) $this->_leftExpr; + + if ($this->_leftExpr instanceof Math) { + $leftExpr = '(' . $leftExpr . ')'; + } + + // Adjusting Right Expression + $rightExpr = (string) $this->_rightExpr; + + if ($this->_rightExpr instanceof Math) { + $rightExpr = '(' . $rightExpr . ')'; + } + + return $leftExpr . ' ' . $this->_operator . ' ' . $rightExpr; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/OrderBy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/OrderBy.php new file mode 100644 index 0000000..a24e286 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/OrderBy.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL Order By parts + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class OrderBy +{ + protected $_preSeparator = ''; + protected $_separator = ', '; + protected $_postSeparator = ''; + protected $_allowedClasses = array(); + + private $_parts = array(); + + public function __construct($sort = null, $order = null) + { + if ($sort) { + $this->add($sort, $order); + } + } + + public function add($sort, $order = null) + { + $order = ! $order ? 'ASC' : $order; + $this->_parts[] = $sort . ' '. $order; + } + + public function count() + { + return count($this->_parts); + } + + public function __tostring() + { + return $this->_preSeparator . implode($this->_separator, $this->_parts) . $this->_postSeparator; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Orx.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Orx.php new file mode 100644 index 0000000..742e499 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Orx.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL OR clauses + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Orx extends Composite +{ + protected $_separator = ' OR '; + protected $_allowedClasses = array( + 'Doctrine\ORM\Query\Expr\Comparison', + 'Doctrine\ORM\Query\Expr\Func', + 'Doctrine\ORM\Query\Expr\Andx', + 'Doctrine\ORM\Query\Expr\Orx', + ); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Select.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Select.php new file mode 100644 index 0000000..a310a0c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Select.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL select statements + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Select extends Base +{ + protected $_preSeparator = ''; + protected $_postSeparator = ''; + protected $_allowedClasses = array( + 'Doctrine\ORM\Query\Expr\Func' + ); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Filter/SQLFilter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Filter/SQLFilter.php new file mode 100644 index 0000000..a87dd84 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Filter/SQLFilter.php @@ -0,0 +1,122 @@ +. + */ + +namespace Doctrine\ORM\Query\Filter; + +use Doctrine\ORM\EntityManager, + Doctrine\ORM\Mapping\ClassMetaData, + Doctrine\ORM\Query\ParameterTypeInferer; + +/** + * The base class that user defined filters should extend. + * + * Handles the setting and escaping of parameters. + * + * @author Alexander + * @author Benjamin Eberlei + * @abstract + */ +abstract class SQLFilter +{ + /** + * The entity manager. + * @var EntityManager + */ + private $em; + + /** + * Parameters for the filter. + * @var array + */ + private $parameters; + + /** + * Constructs the SQLFilter object. + * + * @param EntityManager $em The EM + */ + final public function __construct(EntityManager $em) + { + $this->em = $em; + } + + /** + * Sets a parameter that can be used by the filter. + * + * @param string $name Name of the parameter. + * @param string $value Value of the parameter. + * @param string $type The parameter type. If specified, the given value will be run through + * the type conversion of this type. This is usually not needed for + * strings and numeric types. + * + * @return SQLFilter The current SQL filter. + */ + final public function setParameter($name, $value, $type = null) + { + if (null === $type) { + $type = ParameterTypeInferer::inferType($value); + } + + $this->parameters[$name] = array('value' => $value, 'type' => $type); + + // Keep the parameters sorted for the hash + ksort($this->parameters); + + // The filter collection of the EM is now dirty + $this->em->getFilters()->setFiltersStateDirty(); + + return $this; + } + + /** + * Gets a parameter to use in a query. + * + * The function is responsible for the right output escaping to use the + * value in a query. + * + * @param string $name Name of the parameter. + * + * @return string The SQL escaped parameter to use in a query. + */ + final public function getParameter($name) + { + if (!isset($this->parameters[$name])) { + throw new \InvalidArgumentException("Parameter '" . $name . "' does not exist."); + } + + return $this->em->getConnection()->quote($this->parameters[$name]['value'], $this->parameters[$name]['type']); + } + + /** + * Returns as string representation of the SQLFilter parameters (the state). + * + * @return string String representation of the SQLFilter. + */ + final public function __toString() + { + return serialize($this->parameters); + } + + /** + * Gets the SQL query part to add to a query. + * + * @return string The constraint SQL if there is available, empty string otherwise + */ + abstract public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/FilterCollection.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/FilterCollection.php new file mode 100644 index 0000000..35c8d04 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/FilterCollection.php @@ -0,0 +1,198 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\Configuration, + Doctrine\ORM\EntityManager; + +/** + * Collection class for all the query filters. + * + * @author Alexander + */ +class FilterCollection +{ + /* Filter STATES */ + /** + * A filter object is in CLEAN state when it has no changed parameters. + */ + const FILTERS_STATE_CLEAN = 1; + + /** + * A filter object is in DIRTY state when it has changed parameters. + */ + const FILTERS_STATE_DIRTY = 2; + + /** + * The used Configuration. + * + * @var Doctrine\ORM\Configuration + */ + private $config; + + /** + * The EntityManager that "owns" this FilterCollection instance. + * + * @var Doctrine\ORM\EntityManager + */ + private $em; + + /** + * Instances of enabled filters. + * + * @var array + */ + private $enabledFilters = array(); + + /** + * @var string The filter hash from the last time the query was parsed. + */ + private $filterHash; + + /** + * @var integer $state The current state of this filter + */ + private $filtersState = self::FILTERS_STATE_CLEAN; + + /** + * Constructor. + * + * @param EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + $this->config = $em->getConfiguration(); + } + + /** + * Get all the enabled filters. + * + * @return array The enabled filters. + */ + public function getEnabledFilters() + { + return $this->enabledFilters; + } + + /** + * Enables a filter from the collection. + * + * @param string $name Name of the filter. + * + * @throws \InvalidArgumentException If the filter does not exist. + * + * @return SQLFilter The enabled filter. + */ + public function enable($name) + { + if (null === $filterClass = $this->config->getFilterClassName($name)) { + throw new \InvalidArgumentException("Filter '" . $name . "' does not exist."); + } + + if (!isset($this->enabledFilters[$name])) { + $this->enabledFilters[$name] = new $filterClass($this->em); + + // Keep the enabled filters sorted for the hash + ksort($this->enabledFilters); + + // Now the filter collection is dirty + $this->filtersState = self::FILTERS_STATE_DIRTY; + } + + return $this->enabledFilters[$name]; + } + + /** + * Disables a filter. + * + * @param string $name Name of the filter. + * + * @return SQLFilter The disabled filter. + * + * @throws \InvalidArgumentException If the filter does not exist. + */ + public function disable($name) + { + // Get the filter to return it + $filter = $this->getFilter($name); + + unset($this->enabledFilters[$name]); + + // Now the filter collection is dirty + $this->filtersState = self::FILTERS_STATE_DIRTY; + + return $filter; + } + + /** + * Get an enabled filter from the collection. + * + * @param string $name Name of the filter. + * + * @return SQLFilter The filter. + * + * @throws \InvalidArgumentException If the filter is not enabled. + */ + public function getFilter($name) + { + if (!isset($this->enabledFilters[$name])) { + throw new \InvalidArgumentException("Filter '" . $name . "' is not enabled."); + } + + return $this->enabledFilters[$name]; + } + + /** + * @return boolean True, if the filter collection is clean. + */ + public function isClean() + { + return self::FILTERS_STATE_CLEAN === $this->filtersState; + } + + /** + * Generates a string of currently enabled filters to use for the cache id. + * + * @return string + */ + public function getHash() + { + // If there are only clean filters, the previous hash can be returned + if (self::FILTERS_STATE_CLEAN === $this->filtersState) { + return $this->filterHash; + } + + $filterHash = ''; + foreach ($this->enabledFilters as $name => $filter) { + $filterHash .= $name . $filter; + } + + return $filterHash; + } + + /** + * Set the filter state to dirty. + */ + public function setFiltersStateDirty() + { + $this->filtersState = self::FILTERS_STATE_DIRTY; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Lexer.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Lexer.php new file mode 100644 index 0000000..1eef6af --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Lexer.php @@ -0,0 +1,208 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Scans a DQL query for tokens. + * + * @author Guilherme Blanco + * @author Janne Vanhala + * @author Roman Borschel + * @since 2.0 + */ +class Lexer extends \Doctrine\Common\Lexer +{ + // All tokens that are not valid identifiers must be < 100 + const T_NONE = 1; + const T_INTEGER = 2; + const T_STRING = 3; + const T_INPUT_PARAMETER = 4; + const T_FLOAT = 5; + const T_CLOSE_PARENTHESIS = 6; + const T_OPEN_PARENTHESIS = 7; + const T_COMMA = 8; + const T_DIVIDE = 9; + const T_DOT = 10; + const T_EQUALS = 11; + const T_GREATER_THAN = 12; + const T_LOWER_THAN = 13; + const T_MINUS = 14; + const T_MULTIPLY = 15; + const T_NEGATE = 16; + const T_PLUS = 17; + const T_OPEN_CURLY_BRACE = 18; + const T_CLOSE_CURLY_BRACE = 19; + + // All tokens that are also identifiers should be >= 100 + const T_IDENTIFIER = 100; + const T_ALL = 101; + const T_AND = 102; + const T_ANY = 103; + const T_AS = 104; + const T_ASC = 105; + const T_AVG = 106; + const T_BETWEEN = 107; + const T_BOTH = 108; + const T_BY = 109; + const T_CASE = 110; + const T_COALESCE = 111; + const T_COUNT = 112; + const T_DELETE = 113; + const T_DESC = 114; + const T_DISTINCT = 115; + const T_ELSE = 116; + const T_EMPTY = 117; + const T_END = 118; + const T_ESCAPE = 119; + const T_EXISTS = 120; + const T_FALSE = 121; + const T_FROM = 122; + const T_GROUP = 123; + const T_HAVING = 124; + const T_HIDDEN = 125; + const T_IN = 126; + const T_INDEX = 127; + const T_INNER = 128; + const T_INSTANCE = 129; + const T_IS = 130; + const T_JOIN = 131; + const T_LEADING = 132; + const T_LEFT = 133; + const T_LIKE = 134; + const T_MAX = 135; + const T_MEMBER = 136; + const T_MIN = 137; + const T_NOT = 138; + const T_NULL = 139; + const T_NULLIF = 140; + const T_OF = 141; + const T_OR = 142; + const T_ORDER = 143; + const T_OUTER = 144; + const T_SELECT = 145; + const T_SET = 146; + const T_SIZE = 147; + const T_SOME = 148; + const T_SUM = 149; + const T_THEN = 150; + const T_TRAILING = 151; + const T_TRUE = 152; + const T_UPDATE = 153; + const T_WHEN = 154; + const T_WHERE = 155; + const T_WITH = 156; + const T_PARTIAL = 157; + const T_MOD = 158; + + /** + * Creates a new query scanner object. + * + * @param string $input a query string + */ + public function __construct($input) + { + $this->setInput($input); + } + + /** + * @inheritdoc + */ + protected function getCatchablePatterns() + { + return array( + '[a-z_\\\][a-z0-9_\:\\\]*[a-z0-9_]{1}', + '(?:[0-9]+(?:[\.][0-9]+)*)(?:e[+-]?[0-9]+)?', + "'(?:[^']|'')*'", + '\?[0-9]*|:[a-z]{1}[a-z0-9_]{0,}' + ); + } + + /** + * @inheritdoc + */ + protected function getNonCatchablePatterns() + { + return array('\s+', '(.)'); + } + + /** + * @inheritdoc + */ + protected function getType(&$value) + { + $type = self::T_NONE; + + switch (true) { + // Recognize numeric values + case (is_numeric($value)): + if (strpos($value, '.') !== false || stripos($value, 'e') !== false) { + return self::T_FLOAT; + } + + return self::T_INTEGER; + + // Recognize quoted strings + case ($value[0] === "'"): + $value = str_replace("''", "'", substr($value, 1, strlen($value) - 2)); + + return self::T_STRING; + + // Recognize identifiers + case (ctype_alpha($value[0]) || $value[0] === '_'): + $name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($value); + + if (defined($name)) { + $type = constant($name); + + if ($type > 100) { + return $type; + } + } + + return self::T_IDENTIFIER; + + // Recognize input parameters + case ($value[0] === '?' || $value[0] === ':'): + return self::T_INPUT_PARAMETER; + + // Recognize symbols + case ($value === '.'): return self::T_DOT; + case ($value === ','): return self::T_COMMA; + case ($value === '('): return self::T_OPEN_PARENTHESIS; + case ($value === ')'): return self::T_CLOSE_PARENTHESIS; + case ($value === '='): return self::T_EQUALS; + case ($value === '>'): return self::T_GREATER_THAN; + case ($value === '<'): return self::T_LOWER_THAN; + case ($value === '+'): return self::T_PLUS; + case ($value === '-'): return self::T_MINUS; + case ($value === '*'): return self::T_MULTIPLY; + case ($value === '/'): return self::T_DIVIDE; + case ($value === '!'): return self::T_NEGATE; + case ($value === '{'): return self::T_OPEN_CURLY_BRACE; + case ($value === '}'): return self::T_CLOSE_CURLY_BRACE; + + // Default + default: + // Do nothing + } + + return $type; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParameterTypeInferer.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParameterTypeInferer.php new file mode 100644 index 0000000..39aef29 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParameterTypeInferer.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\DBAL\Connection, + Doctrine\DBAL\Types\Type; + +/** + * Provides an enclosed support for parameter infering. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ParameterTypeInferer +{ + /** + * Infer type of a given value, returning a compatible constant: + * - Type (\Doctrine\DBAL\Types\Type::*) + * - Connection (\Doctrine\DBAL\Connection::PARAM_*) + * + * @param mixed $value Parameter value + * + * @return mixed Parameter type constant + */ + public static function inferType($value) + { + switch (true) { + case is_integer($value): + return Type::INTEGER; + + case ($value instanceof \DateTime): + return Type::DATETIME; + + case is_array($value): + $key = key($value); + + if (is_integer($value[$key])) { + return Connection::PARAM_INT_ARRAY; + } + + return Connection::PARAM_STR_ARRAY; + + default: + // Do nothing + break; + } + + return \PDO::PARAM_STR; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php new file mode 100644 index 0000000..4200836 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php @@ -0,0 +1,3093 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\Query; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language. + * Parses a DQL query, reports any errors in it, and generates an AST. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Janne Vanhala + */ +class Parser +{ + /** READ-ONLY: Maps BUILT-IN string function names to AST class names. */ + private static $_STRING_FUNCTIONS = array( + 'concat' => 'Doctrine\ORM\Query\AST\Functions\ConcatFunction', + 'substring' => 'Doctrine\ORM\Query\AST\Functions\SubstringFunction', + 'trim' => 'Doctrine\ORM\Query\AST\Functions\TrimFunction', + 'lower' => 'Doctrine\ORM\Query\AST\Functions\LowerFunction', + 'upper' => 'Doctrine\ORM\Query\AST\Functions\UpperFunction', + 'identity' => 'Doctrine\ORM\Query\AST\Functions\IdentityFunction', + ); + + /** READ-ONLY: Maps BUILT-IN numeric function names to AST class names. */ + private static $_NUMERIC_FUNCTIONS = array( + 'length' => 'Doctrine\ORM\Query\AST\Functions\LengthFunction', + 'locate' => 'Doctrine\ORM\Query\AST\Functions\LocateFunction', + 'abs' => 'Doctrine\ORM\Query\AST\Functions\AbsFunction', + 'sqrt' => 'Doctrine\ORM\Query\AST\Functions\SqrtFunction', + 'mod' => 'Doctrine\ORM\Query\AST\Functions\ModFunction', + 'size' => 'Doctrine\ORM\Query\AST\Functions\SizeFunction', + 'date_diff' => 'Doctrine\ORM\Query\AST\Functions\DateDiffFunction', + 'bit_and' => 'Doctrine\ORM\Query\AST\Functions\BitAndFunction', + 'bit_or' => 'Doctrine\ORM\Query\AST\Functions\BitOrFunction', + ); + + /** READ-ONLY: Maps BUILT-IN datetime function names to AST class names. */ + private static $_DATETIME_FUNCTIONS = array( + 'current_date' => 'Doctrine\ORM\Query\AST\Functions\CurrentDateFunction', + 'current_time' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimeFunction', + 'current_timestamp' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimestampFunction', + 'date_add' => 'Doctrine\ORM\Query\AST\Functions\DateAddFunction', + 'date_sub' => 'Doctrine\ORM\Query\AST\Functions\DateSubFunction', + ); + + /** + * Expressions that were encountered during parsing of identifiers and expressions + * and still need to be validated. + */ + private $_deferredIdentificationVariables = array(); + private $_deferredPartialObjectExpressions = array(); + private $_deferredPathExpressions = array(); + private $_deferredResultVariables = array(); + + /** + * The lexer. + * + * @var \Doctrine\ORM\Query\Lexer + */ + private $_lexer; + + /** + * The parser result. + * + * @var \Doctrine\ORM\Query\ParserResult + */ + private $_parserResult; + + /** + * The EntityManager. + * + * @var EnityManager + */ + private $_em; + + /** + * The Query to parse. + * + * @var Query + */ + private $_query; + + /** + * Map of declared query components in the parsed query. + * + * @var array + */ + private $_queryComponents = array(); + + /** + * Keeps the nesting level of defined ResultVariables + * + * @var integer + */ + private $_nestingLevel = 0; + + /** + * Any additional custom tree walkers that modify the AST. + * + * @var array + */ + private $_customTreeWalkers = array(); + + /** + * The custom last tree walker, if any, that is responsible for producing the output. + * + * @var TreeWalker + */ + private $_customOutputWalker; + + /** + * @var array + */ + private $_identVariableExpressions = array(); + + /** + * Creates a new query parser object. + * + * @param Query $query The Query to parse. + */ + public function __construct(Query $query) + { + $this->_query = $query; + $this->_em = $query->getEntityManager(); + $this->_lexer = new Lexer($query->getDql()); + $this->_parserResult = new ParserResult(); + } + + /** + * Sets a custom tree walker that produces output. + * This tree walker will be run last over the AST, after any other walkers. + * + * @param string $className + */ + public function setCustomOutputTreeWalker($className) + { + $this->_customOutputWalker = $className; + } + + /** + * Adds a custom tree walker for modifying the AST. + * + * @param string $className + */ + public function addCustomTreeWalker($className) + { + $this->_customTreeWalkers[] = $className; + } + + /** + * Gets the lexer used by the parser. + * + * @return \Doctrine\ORM\Query\Lexer + */ + public function getLexer() + { + return $this->_lexer; + } + + /** + * Gets the ParserResult that is being filled with information during parsing. + * + * @return \Doctrine\ORM\Query\ParserResult + */ + public function getParserResult() + { + return $this->_parserResult; + } + + /** + * Gets the EntityManager used by the parser. + * + * @return EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * Parse and build AST for the given Query. + * + * @return \Doctrine\ORM\Query\AST\SelectStatement | + * \Doctrine\ORM\Query\AST\UpdateStatement | + * \Doctrine\ORM\Query\AST\DeleteStatement + */ + public function getAST() + { + // Parse & build AST + $AST = $this->QueryLanguage(); + + // Process any deferred validations of some nodes in the AST. + // This also allows post-processing of the AST for modification purposes. + $this->_processDeferredIdentificationVariables(); + + if ($this->_deferredPartialObjectExpressions) { + $this->_processDeferredPartialObjectExpressions(); + } + + if ($this->_deferredPathExpressions) { + $this->_processDeferredPathExpressions($AST); + } + + if ($this->_deferredResultVariables) { + $this->_processDeferredResultVariables(); + } + + $this->_processRootEntityAliasSelected(); + + // TODO: Is there a way to remove this? It may impact the mixed hydration resultset a lot! + $this->fixIdentificationVariableOrder($AST); + + return $AST; + } + + /** + * Attempts to match the given token with the current lookahead token. + * + * If they match, updates the lookahead token; otherwise raises a syntax + * error. + * + * @param int token type + * @return void + * @throws QueryException If the tokens dont match. + */ + public function match($token) + { + $lookaheadType = $this->_lexer->lookahead['type']; + + // short-circuit on first condition, usually types match + if ($lookaheadType !== $token && $token !== Lexer::T_IDENTIFIER && $lookaheadType <= Lexer::T_IDENTIFIER) { + $this->syntaxError($this->_lexer->getLiteral($token)); + } + + $this->_lexer->moveNext(); + } + + /** + * Free this parser enabling it to be reused + * + * @param boolean $deep Whether to clean peek and reset errors + * @param integer $position Position to reset + */ + public function free($deep = false, $position = 0) + { + // WARNING! Use this method with care. It resets the scanner! + $this->_lexer->resetPosition($position); + + // Deep = true cleans peek and also any previously defined errors + if ($deep) { + $this->_lexer->resetPeek(); + } + + $this->_lexer->token = null; + $this->_lexer->lookahead = null; + } + + /** + * Parses a query string. + * + * @return ParserResult + */ + public function parse() + { + $AST = $this->getAST(); + + if (($customWalkers = $this->_query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) { + $this->_customTreeWalkers = $customWalkers; + } + + if (($customOutputWalker = $this->_query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER)) !== false) { + $this->_customOutputWalker = $customOutputWalker; + } + + // Run any custom tree walkers over the AST + if ($this->_customTreeWalkers) { + $treeWalkerChain = new TreeWalkerChain($this->_query, $this->_parserResult, $this->_queryComponents); + + foreach ($this->_customTreeWalkers as $walker) { + $treeWalkerChain->addTreeWalker($walker); + } + + switch (true) { + case ($AST instanceof AST\UpdateStatement): + $treeWalkerChain->walkUpdateStatement($AST); + break; + + case ($AST instanceof AST\DeleteStatement): + $treeWalkerChain->walkDeleteStatement($AST); + break; + + case ($AST instanceof AST\SelectStatement): + default: + $treeWalkerChain->walkSelectStatement($AST); + } + } + + $outputWalkerClass = $this->_customOutputWalker ?: __NAMESPACE__ . '\SqlWalker'; + $outputWalker = new $outputWalkerClass($this->_query, $this->_parserResult, $this->_queryComponents); + + // Assign an SQL executor to the parser result + $this->_parserResult->setSqlExecutor($outputWalker->getExecutor($AST)); + + return $this->_parserResult; + } + + /** + * Fix order of identification variables. + * + * They have to appear in the select clause in the same order as the + * declarations (from ... x join ... y join ... z ...) appear in the query + * as the hydration process relies on that order for proper operation. + * + * @param AST\SelectStatement|AST\DeleteStatement|AST\UpdateStatement $AST + * @return void + */ + private function fixIdentificationVariableOrder($AST) + { + if (count($this->_identVariableExpressions) <= 1) { + return; + } + + foreach ($this->_queryComponents as $dqlAlias => $qComp) { + if ( ! isset($this->_identVariableExpressions[$dqlAlias])) { + continue; + } + + $expr = $this->_identVariableExpressions[$dqlAlias]; + $key = array_search($expr, $AST->selectClause->selectExpressions); + + unset($AST->selectClause->selectExpressions[$key]); + + $AST->selectClause->selectExpressions[] = $expr; + } + } + + /** + * Generates a new syntax error. + * + * @param string $expected Expected string. + * @param array $token Got token. + * + * @throws \Doctrine\ORM\Query\QueryException + */ + public function syntaxError($expected = '', $token = null) + { + if ($token === null) { + $token = $this->_lexer->lookahead; + } + + $tokenPos = (isset($token['position'])) ? $token['position'] : '-1'; + + $message = "line 0, col {$tokenPos}: Error: "; + $message .= ($expected !== '') ? "Expected {$expected}, got " : 'Unexpected '; + $message .= ($this->_lexer->lookahead === null) ? 'end of string.' : "'{$token['value']}'"; + + throw QueryException::syntaxError($message); + } + + /** + * Generates a new semantical error. + * + * @param string $message Optional message. + * @param array $token Optional token. + * + * @throws \Doctrine\ORM\Query\QueryException + */ + public function semanticalError($message = '', $token = null) + { + if ($token === null) { + $token = $this->_lexer->lookahead; + } + + // Minimum exposed chars ahead of token + $distance = 12; + + // Find a position of a final word to display in error string + $dql = $this->_query->getDql(); + $length = strlen($dql); + $pos = $token['position'] + $distance; + $pos = strpos($dql, ' ', ($length > $pos) ? $pos : $length); + $length = ($pos !== false) ? $pos - $token['position'] : $distance; + + $tokenPos = (isset($token['position']) && $token['position'] > 0) ? $token['position'] : '-1'; + $tokenStr = substr($dql, $token['position'], $length); + + // Building informative message + $message = 'line 0, col ' . $tokenPos . " near '" . $tokenStr . "': Error: " . $message; + + throw QueryException::semanticalError($message); + } + + /** + * Peeks beyond the specified token and returns the first token after that one. + * + * @param array $token + * @return array + */ + private function _peekBeyond($token) + { + $peek = $this->_lexer->peek(); + + while ($peek['value'] != $token) { + $peek = $this->_lexer->peek(); + } + + $peek = $this->_lexer->peek(); + $this->_lexer->resetPeek(); + + return $peek; + } + + /** + * Peek beyond the matched closing parenthesis and return the first token after that one. + * + * @return array + */ + private function _peekBeyondClosingParenthesis() + { + $token = $this->_lexer->peek(); + $numUnmatched = 1; + + while ($numUnmatched > 0 && $token !== null) { + switch ($token['type']) { + case Lexer::T_OPEN_PARENTHESIS: + ++$numUnmatched; + break; + + case Lexer::T_CLOSE_PARENTHESIS: + --$numUnmatched; + break; + + default: + // Do nothing + } + + $token = $this->_lexer->peek(); + } + + $this->_lexer->resetPeek(); + + return $token; + } + + /** + * Checks if the given token indicates a mathematical operator. + * + * @return boolean TRUE if the token is a mathematical operator, FALSE otherwise. + */ + private function _isMathOperator($token) + { + return in_array($token['type'], array(Lexer::T_PLUS, Lexer::T_MINUS, Lexer::T_DIVIDE, Lexer::T_MULTIPLY)); + } + + /** + * Checks if the next-next (after lookahead) token starts a function. + * + * @return boolean TRUE if the next-next tokens start a function, FALSE otherwise. + */ + private function _isFunction() + { + $peek = $this->_lexer->peek(); + $nextpeek = $this->_lexer->peek(); + + $this->_lexer->resetPeek(); + + // We deny the COUNT(SELECT * FROM User u) here. COUNT won't be considered a function + return ($peek['type'] === Lexer::T_OPEN_PARENTHESIS && $nextpeek['type'] !== Lexer::T_SELECT); + } + + /** + * Checks whether the given token type indicates an aggregate function. + * + * @return boolean TRUE if the token type is an aggregate function, FALSE otherwise. + */ + private function _isAggregateFunction($tokenType) + { + return in_array($tokenType, array(Lexer::T_AVG, Lexer::T_MIN, Lexer::T_MAX, Lexer::T_SUM, Lexer::T_COUNT)); + } + + /** + * Checks whether the current lookahead token of the lexer has the type T_ALL, T_ANY or T_SOME. + * + * @return boolean + */ + private function _isNextAllAnySome() + { + return in_array($this->_lexer->lookahead['type'], array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME)); + } + + /** + * Validates that the given IdentificationVariable is semantically correct. + * It must exist in query components list. + * + * @return void + */ + private function _processDeferredIdentificationVariables() + { + foreach ($this->_deferredIdentificationVariables as $deferredItem) { + $identVariable = $deferredItem['expression']; + + // Check if IdentificationVariable exists in queryComponents + if ( ! isset($this->_queryComponents[$identVariable])) { + $this->semanticalError( + "'$identVariable' is not defined.", $deferredItem['token'] + ); + } + + $qComp = $this->_queryComponents[$identVariable]; + + // Check if queryComponent points to an AbstractSchemaName or a ResultVariable + if ( ! isset($qComp['metadata'])) { + $this->semanticalError( + "'$identVariable' does not point to a Class.", $deferredItem['token'] + ); + } + + // Validate if identification variable nesting level is lower or equal than the current one + if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) { + $this->semanticalError( + "'$identVariable' is used outside the scope of its declaration.", $deferredItem['token'] + ); + } + } + } + + /** + * Validates that the given PartialObjectExpression is semantically correct. + * It must exist in query components list. + * + * @return void + */ + private function _processDeferredPartialObjectExpressions() + { + foreach ($this->_deferredPartialObjectExpressions as $deferredItem) { + $expr = $deferredItem['expression']; + $class = $this->_queryComponents[$expr->identificationVariable]['metadata']; + + foreach ($expr->partialFieldSet as $field) { + if (isset($class->fieldMappings[$field])) { + continue; + } + + $this->semanticalError( + "There is no mapped field named '$field' on class " . $class->name . ".", $deferredItem['token'] + ); + } + + if (array_intersect($class->identifier, $expr->partialFieldSet) != $class->identifier) { + $this->semanticalError( + "The partial field selection of class " . $class->name . " must contain the identifier.", + $deferredItem['token'] + ); + } + } + } + + /** + * Validates that the given ResultVariable is semantically correct. + * It must exist in query components list. + * + * @return void + */ + private function _processDeferredResultVariables() + { + foreach ($this->_deferredResultVariables as $deferredItem) { + $resultVariable = $deferredItem['expression']; + + // Check if ResultVariable exists in queryComponents + if ( ! isset($this->_queryComponents[$resultVariable])) { + $this->semanticalError( + "'$resultVariable' is not defined.", $deferredItem['token'] + ); + } + + $qComp = $this->_queryComponents[$resultVariable]; + + // Check if queryComponent points to an AbstractSchemaName or a ResultVariable + if ( ! isset($qComp['resultVariable'])) { + $this->semanticalError( + "'$identVariable' does not point to a ResultVariable.", $deferredItem['token'] + ); + } + + // Validate if identification variable nesting level is lower or equal than the current one + if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) { + $this->semanticalError( + "'$resultVariable' is used outside the scope of its declaration.", $deferredItem['token'] + ); + } + } + } + + /** + * Validates that the given PathExpression is semantically correct for grammar rules: + * + * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression + * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression + * StateFieldPathExpression ::= IdentificationVariable "." StateField + * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField + * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField + * + * @param array $deferredItem + * @param mixed $AST + */ + private function _processDeferredPathExpressions($AST) + { + foreach ($this->_deferredPathExpressions as $deferredItem) { + $pathExpression = $deferredItem['expression']; + + $qComp = $this->_queryComponents[$pathExpression->identificationVariable]; + $class = $qComp['metadata']; + + if (($field = $pathExpression->field) === null) { + $field = $pathExpression->field = $class->identifier[0]; + } + + // Check if field or association exists + if ( ! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) { + $this->semanticalError( + 'Class ' . $class->name . ' has no field or association named ' . $field, + $deferredItem['token'] + ); + } + + $fieldType = AST\PathExpression::TYPE_STATE_FIELD; + + if (isset($class->associationMappings[$field])) { + $assoc = $class->associationMappings[$field]; + + $fieldType = ($assoc['type'] & ClassMetadata::TO_ONE) + ? AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION + : AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION; + } + + // Validate if PathExpression is one of the expected types + $expectedType = $pathExpression->expectedType; + + if ( ! ($expectedType & $fieldType)) { + // We need to recognize which was expected type(s) + $expectedStringTypes = array(); + + // Validate state field type + if ($expectedType & AST\PathExpression::TYPE_STATE_FIELD) { + $expectedStringTypes[] = 'StateFieldPathExpression'; + } + + // Validate single valued association (*-to-one) + if ($expectedType & AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION) { + $expectedStringTypes[] = 'SingleValuedAssociationField'; + } + + // Validate single valued association (*-to-many) + if ($expectedType & AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION) { + $expectedStringTypes[] = 'CollectionValuedAssociationField'; + } + + // Build the error message + $semanticalError = 'Invalid PathExpression. '; + $semanticalError .= (count($expectedStringTypes) == 1) + ? 'Must be a ' . $expectedStringTypes[0] . '.' + : implode(' or ', $expectedStringTypes) . ' expected.'; + + $this->semanticalError($semanticalError, $deferredItem['token']); + } + + // We need to force the type in PathExpression + $pathExpression->type = $fieldType; + } + } + + private function _processRootEntityAliasSelected() + { + if ( ! count($this->_identVariableExpressions)) { + return; + } + + $foundRootEntity = false; + + foreach ($this->_identVariableExpressions AS $dqlAlias => $expr) { + if (isset($this->_queryComponents[$dqlAlias]) && $this->_queryComponents[$dqlAlias]['parent'] === null) { + $foundRootEntity = true; + } + } + + if ( ! $foundRootEntity) { + $this->semanticalError('Cannot select entity through identification variables without choosing at least one root entity alias.'); + } + } + + /** + * QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement + * + * @return \Doctrine\ORM\Query\AST\SelectStatement | + * \Doctrine\ORM\Query\AST\UpdateStatement | + * \Doctrine\ORM\Query\AST\DeleteStatement + */ + public function QueryLanguage() + { + $this->_lexer->moveNext(); + + switch ($this->_lexer->lookahead['type']) { + case Lexer::T_SELECT: + $statement = $this->SelectStatement(); + break; + + case Lexer::T_UPDATE: + $statement = $this->UpdateStatement(); + break; + + case Lexer::T_DELETE: + $statement = $this->DeleteStatement(); + break; + + default: + $this->syntaxError('SELECT, UPDATE or DELETE'); + break; + } + + // Check for end of string + if ($this->_lexer->lookahead !== null) { + $this->syntaxError('end of string'); + } + + return $statement; + } + + /** + * SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @return \Doctrine\ORM\Query\AST\SelectStatement + */ + public function SelectStatement() + { + $selectStatement = new AST\SelectStatement($this->SelectClause(), $this->FromClause()); + + $selectStatement->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + $selectStatement->groupByClause = $this->_lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null; + $selectStatement->havingClause = $this->_lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null; + $selectStatement->orderByClause = $this->_lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null; + + return $selectStatement; + } + + /** + * UpdateStatement ::= UpdateClause [WhereClause] + * + * @return \Doctrine\ORM\Query\AST\UpdateStatement + */ + public function UpdateStatement() + { + $updateStatement = new AST\UpdateStatement($this->UpdateClause()); + + $updateStatement->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + + return $updateStatement; + } + + /** + * DeleteStatement ::= DeleteClause [WhereClause] + * + * @return \Doctrine\ORM\Query\AST\DeleteStatement + */ + public function DeleteStatement() + { + $deleteStatement = new AST\DeleteStatement($this->DeleteClause()); + + $deleteStatement->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + + return $deleteStatement; + } + + /** + * IdentificationVariable ::= identifier + * + * @return string + */ + public function IdentificationVariable() + { + $this->match(Lexer::T_IDENTIFIER); + + $identVariable = $this->_lexer->token['value']; + + $this->_deferredIdentificationVariables[] = array( + 'expression' => $identVariable, + 'nestingLevel' => $this->_nestingLevel, + 'token' => $this->_lexer->token, + ); + + return $identVariable; + } + + /** + * AliasIdentificationVariable = identifier + * + * @return string + */ + public function AliasIdentificationVariable() + { + $this->match(Lexer::T_IDENTIFIER); + + $aliasIdentVariable = $this->_lexer->token['value']; + $exists = isset($this->_queryComponents[$aliasIdentVariable]); + + if ($exists) { + $this->semanticalError("'$aliasIdentVariable' is already defined.", $this->_lexer->token); + } + + return $aliasIdentVariable; + } + + /** + * AbstractSchemaName ::= identifier + * + * @return string + */ + public function AbstractSchemaName() + { + $this->match(Lexer::T_IDENTIFIER); + + $schemaName = ltrim($this->_lexer->token['value'], '\\'); + + if (strrpos($schemaName, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $schemaName); + + $schemaName = $this->_em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + $exists = class_exists($schemaName, true); + + if ( ! $exists) { + $this->semanticalError("Class '$schemaName' is not defined.", $this->_lexer->token); + } + + return $schemaName; + } + + /** + * AliasResultVariable ::= identifier + * + * @return string + */ + public function AliasResultVariable() + { + $this->match(Lexer::T_IDENTIFIER); + + $resultVariable = $this->_lexer->token['value']; + $exists = isset($this->_queryComponents[$resultVariable]); + + if ($exists) { + $this->semanticalError("'$resultVariable' is already defined.", $this->_lexer->token); + } + + return $resultVariable; + } + + /** + * ResultVariable ::= identifier + * + * @return string + */ + public function ResultVariable() + { + $this->match(Lexer::T_IDENTIFIER); + + $resultVariable = $this->_lexer->token['value']; + + // Defer ResultVariable validation + $this->_deferredResultVariables[] = array( + 'expression' => $resultVariable, + 'nestingLevel' => $this->_nestingLevel, + 'token' => $this->_lexer->token, + ); + + return $resultVariable; + } + + /** + * JoinAssociationPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField) + * + * @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression + */ + public function JoinAssociationPathExpression() + { + $token = $this->_lexer->lookahead; + $identVariable = $this->IdentificationVariable(); + + if ( ! isset($this->_queryComponents[$identVariable])) { + $this->semanticalError( + 'Identification Variable ' . $identVariable .' used in join path expression but was not defined before.' + ); + } + + $this->match(Lexer::T_DOT); + $this->match(Lexer::T_IDENTIFIER); + + $field = $this->_lexer->token['value']; + + // Validate association field + $qComp = $this->_queryComponents[$identVariable]; + $class = $qComp['metadata']; + + if ( ! isset($class->associationMappings[$field])) { + $this->semanticalError('Class ' . $class->name . ' has no association named ' . $field); + } + + return new AST\JoinAssociationPathExpression($identVariable, $field); + } + + /** + * Parses an arbitrary path expression and defers semantical validation + * based on expected types. + * + * PathExpression ::= IdentificationVariable "." identifier + * + * @param integer $expectedTypes + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function PathExpression($expectedTypes) + { + $token = $this->_lexer->lookahead; + $identVariable = $this->IdentificationVariable(); + $field = null; + + if ($this->_lexer->isNextToken(Lexer::T_DOT)) { + $this->match(Lexer::T_DOT); + $this->match(Lexer::T_IDENTIFIER); + + $field = $this->_lexer->token['value']; + } + + // Creating AST node + $pathExpr = new AST\PathExpression($expectedTypes, $identVariable, $field); + + // Defer PathExpression validation if requested to be defered + $this->_deferredPathExpressions[] = array( + 'expression' => $pathExpr, + 'nestingLevel' => $this->_nestingLevel, + 'token' => $this->_lexer->token, + ); + + return $pathExpr; + } + + /** + * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function AssociationPathExpression() + { + return $this->PathExpression( + AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION | + AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION + ); + } + + /** + * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function SingleValuedPathExpression() + { + return $this->PathExpression( + AST\PathExpression::TYPE_STATE_FIELD | + AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION + ); + } + + /** + * StateFieldPathExpression ::= IdentificationVariable "." StateField + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function StateFieldPathExpression() + { + return $this->PathExpression(AST\PathExpression::TYPE_STATE_FIELD); + } + + /** + * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function SingleValuedAssociationPathExpression() + { + return $this->PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION); + } + + /** + * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function CollectionValuedPathExpression() + { + return $this->PathExpression(AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION); + } + + /** + * SelectClause ::= "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression} + * + * @return \Doctrine\ORM\Query\AST\SelectClause + */ + public function SelectClause() + { + $isDistinct = false; + $this->match(Lexer::T_SELECT); + + // Check for DISTINCT + if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) { + $this->match(Lexer::T_DISTINCT); + + $isDistinct = true; + } + + // Process SelectExpressions (1..N) + $selectExpressions = array(); + $selectExpressions[] = $this->SelectExpression(); + + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $selectExpressions[] = $this->SelectExpression(); + } + + return new AST\SelectClause($selectExpressions, $isDistinct); + } + + /** + * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression + * + * @return \Doctrine\ORM\Query\AST\SimpleSelectClause + */ + public function SimpleSelectClause() + { + $isDistinct = false; + $this->match(Lexer::T_SELECT); + + if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) { + $this->match(Lexer::T_DISTINCT); + + $isDistinct = true; + } + + return new AST\SimpleSelectClause($this->SimpleSelectExpression(), $isDistinct); + } + + /** + * UpdateClause ::= "UPDATE" AbstractSchemaName ["AS"] AliasIdentificationVariable "SET" UpdateItem {"," UpdateItem}* + * + * @return \Doctrine\ORM\Query\AST\UpdateClause + */ + public function UpdateClause() + { + $this->match(Lexer::T_UPDATE); + $token = $this->_lexer->lookahead; + $abstractSchemaName = $this->AbstractSchemaName(); + + if ($this->_lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + + $class = $this->_em->getClassMetadata($abstractSchemaName); + + // Building queryComponent + $queryComponent = array( + 'metadata' => $class, + 'parent' => null, + 'relation' => null, + 'map' => null, + 'nestingLevel' => $this->_nestingLevel, + 'token' => $token, + ); + + $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent; + + $this->match(Lexer::T_SET); + + $updateItems = array(); + $updateItems[] = $this->UpdateItem(); + + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $updateItems[] = $this->UpdateItem(); + } + + $updateClause = new AST\UpdateClause($abstractSchemaName, $updateItems); + $updateClause->aliasIdentificationVariable = $aliasIdentificationVariable; + + return $updateClause; + } + + /** + * DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName ["AS"] AliasIdentificationVariable + * + * @return \Doctrine\ORM\Query\AST\DeleteClause + */ + public function DeleteClause() + { + $this->match(Lexer::T_DELETE); + + if ($this->_lexer->isNextToken(Lexer::T_FROM)) { + $this->match(Lexer::T_FROM); + } + + $token = $this->_lexer->lookahead; + $deleteClause = new AST\DeleteClause($this->AbstractSchemaName()); + + if ($this->_lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + + $deleteClause->aliasIdentificationVariable = $aliasIdentificationVariable; + $class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName); + + // Building queryComponent + $queryComponent = array( + 'metadata' => $class, + 'parent' => null, + 'relation' => null, + 'map' => null, + 'nestingLevel' => $this->_nestingLevel, + 'token' => $token, + ); + + $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent; + + return $deleteClause; + } + + /** + * FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}* + * + * @return \Doctrine\ORM\Query\AST\FromClause + */ + public function FromClause() + { + $this->match(Lexer::T_FROM); + + $identificationVariableDeclarations = array(); + $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration(); + + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration(); + } + + return new AST\FromClause($identificationVariableDeclarations); + } + + /** + * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* + * + * @return \Doctrine\ORM\Query\AST\SubselectFromClause + */ + public function SubselectFromClause() + { + $this->match(Lexer::T_FROM); + + $identificationVariables = array(); + $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration(); + + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration(); + } + + return new AST\SubselectFromClause($identificationVariables); + } + + /** + * WhereClause ::= "WHERE" ConditionalExpression + * + * @return \Doctrine\ORM\Query\AST\WhereClause + */ + public function WhereClause() + { + $this->match(Lexer::T_WHERE); + + return new AST\WhereClause($this->ConditionalExpression()); + } + + /** + * HavingClause ::= "HAVING" ConditionalExpression + * + * @return \Doctrine\ORM\Query\AST\HavingClause + */ + public function HavingClause() + { + $this->match(Lexer::T_HAVING); + + return new AST\HavingClause($this->ConditionalExpression()); + } + + /** + * GroupByClause ::= "GROUP" "BY" GroupByItem {"," GroupByItem}* + * + * @return \Doctrine\ORM\Query\AST\GroupByClause + */ + public function GroupByClause() + { + $this->match(Lexer::T_GROUP); + $this->match(Lexer::T_BY); + + $groupByItems = array($this->GroupByItem()); + + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $groupByItems[] = $this->GroupByItem(); + } + + return new AST\GroupByClause($groupByItems); + } + + /** + * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}* + * + * @return \Doctrine\ORM\Query\AST\OrderByClause + */ + public function OrderByClause() + { + $this->match(Lexer::T_ORDER); + $this->match(Lexer::T_BY); + + $orderByItems = array(); + $orderByItems[] = $this->OrderByItem(); + + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $orderByItems[] = $this->OrderByItem(); + } + + return new AST\OrderByClause($orderByItems); + } + + /** + * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @return \Doctrine\ORM\Query\AST\Subselect + */ + public function Subselect() + { + // Increase query nesting level + $this->_nestingLevel++; + + $subselect = new AST\Subselect($this->SimpleSelectClause(), $this->SubselectFromClause()); + + $subselect->whereClause = $this->_lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + $subselect->groupByClause = $this->_lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null; + $subselect->havingClause = $this->_lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null; + $subselect->orderByClause = $this->_lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null; + + // Decrease query nesting level + $this->_nestingLevel--; + + return $subselect; + } + + /** + * UpdateItem ::= SingleValuedPathExpression "=" NewValue + * + * @return \Doctrine\ORM\Query\AST\UpdateItem + */ + public function UpdateItem() + { + $pathExpr = $this->SingleValuedPathExpression(); + + $this->match(Lexer::T_EQUALS); + + $updateItem = new AST\UpdateItem($pathExpr, $this->NewValue()); + + return $updateItem; + } + + /** + * GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression + * + * @return string | \Doctrine\ORM\Query\AST\PathExpression + */ + public function GroupByItem() + { + // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression + $glimpse = $this->_lexer->glimpse(); + + if ($glimpse['type'] === Lexer::T_DOT) { + return $this->SingleValuedPathExpression(); + } + + // Still need to decide between IdentificationVariable or ResultVariable + $lookaheadValue = $this->_lexer->lookahead['value']; + + if ( ! isset($this->_queryComponents[$lookaheadValue])) { + $this->semanticalError('Cannot group by undefined identification or result variable.'); + } + + return (isset($this->_queryComponents[$lookaheadValue]['metadata'])) + ? $this->IdentificationVariable() + : $this->ResultVariable(); + } + + /** + * OrderByItem ::= (ResultVariable | SingleValuedPathExpression) ["ASC" | "DESC"] + * + * @return \Doctrine\ORM\Query\AST\OrderByItem + */ + public function OrderByItem() + { + $type = 'ASC'; + + // We need to check if we are in a ResultVariable or StateFieldPathExpression + $glimpse = $this->_lexer->glimpse(); + $expr = ($glimpse['type'] != Lexer::T_DOT) ? $this->ResultVariable() : $this->SingleValuedPathExpression(); + + $item = new AST\OrderByItem($expr); + + switch (true) { + case ($this->_lexer->isNextToken(Lexer::T_DESC)): + $this->match(Lexer::T_DESC); + $type = 'DESC'; + break; + + case ($this->_lexer->isNextToken(Lexer::T_ASC)): + $this->match(Lexer::T_ASC); + break; + + default: + // Do nothing + } + + $item->type = $type; + + return $item; + } + + /** + * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary | + * EnumPrimary | SimpleEntityExpression | "NULL" + * + * NOTE: Since it is not possible to correctly recognize individual types, here is the full + * grammar that needs to be supported: + * + * NewValue ::= SimpleArithmeticExpression | "NULL" + * + * SimpleArithmeticExpression covers all *Primary grammar rules and also SimplEntityExpression + */ + public function NewValue() + { + if ($this->_lexer->isNextToken(Lexer::T_NULL)) { + $this->match(Lexer::T_NULL); + + return null; + } + + if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + $this->match(Lexer::T_INPUT_PARAMETER); + + return new AST\InputParameter($this->_lexer->token['value']); + } + + return $this->SimpleArithmeticExpression(); + } + + /** + * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}* + * + * @return \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration + */ + public function IdentificationVariableDeclaration() + { + $rangeVariableDeclaration = $this->RangeVariableDeclaration(); + $indexBy = $this->_lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null; + $joinVariableDeclarations = array(); + + while ( + $this->_lexer->isNextToken(Lexer::T_LEFT) || + $this->_lexer->isNextToken(Lexer::T_INNER) || + $this->_lexer->isNextToken(Lexer::T_JOIN) + ) { + $joinVariableDeclarations[] = $this->JoinVariableDeclaration(); + } + + return new AST\IdentificationVariableDeclaration( + $rangeVariableDeclaration, $indexBy, $joinVariableDeclarations + ); + } + + /** + * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | (AssociationPathExpression ["AS"] AliasIdentificationVariable) + * + * @return \Doctrine\ORM\Query\AST\SubselectIdentificationVariableDeclaration | + * \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration + */ + public function SubselectIdentificationVariableDeclaration() + { + $glimpse = $this->_lexer->glimpse(); + + /* NOT YET IMPLEMENTED! + + if ($glimpse['type'] == Lexer::T_DOT) { + $subselectIdVarDecl = new AST\SubselectIdentificationVariableDeclaration(); + $subselectIdVarDecl->associationPathExpression = $this->AssociationPathExpression(); + $this->match(Lexer::T_AS); + $subselectIdVarDecl->aliasIdentificationVariable = $this->AliasIdentificationVariable(); + + return $subselectIdVarDecl; + } + */ + + return $this->IdentificationVariableDeclaration(); + } + + /** + * JoinVariableDeclaration ::= Join [IndexBy] + * + * @return \Doctrine\ORM\Query\AST\JoinVariableDeclaration + */ + public function JoinVariableDeclaration() + { + $join = $this->Join(); + $indexBy = $this->_lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null; + + return new AST\JoinVariableDeclaration($join, $indexBy); + } + + /** + * RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable + * + * @return \Doctrine\ORM\Query\AST\RangeVariableDeclaration + */ + public function RangeVariableDeclaration() + { + $abstractSchemaName = $this->AbstractSchemaName(); + + if ($this->_lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $token = $this->_lexer->lookahead; + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + $classMetadata = $this->_em->getClassMetadata($abstractSchemaName); + + // Building queryComponent + $queryComponent = array( + 'metadata' => $classMetadata, + 'parent' => null, + 'relation' => null, + 'map' => null, + 'nestingLevel' => $this->_nestingLevel, + 'token' => $token + ); + + $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent; + + return new AST\RangeVariableDeclaration($abstractSchemaName, $aliasIdentificationVariable); + } + + /** + * PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet + * PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}" + * + * @return array + */ + public function PartialObjectExpression() + { + $this->match(Lexer::T_PARTIAL); + + $partialFieldSet = array(); + + $identificationVariable = $this->IdentificationVariable(); + + $this->match(Lexer::T_DOT); + $this->match(Lexer::T_OPEN_CURLY_BRACE); + $this->match(Lexer::T_IDENTIFIER); + + $partialFieldSet[] = $this->_lexer->token['value']; + + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + $this->match(Lexer::T_IDENTIFIER); + + $partialFieldSet[] = $this->_lexer->token['value']; + } + + $this->match(Lexer::T_CLOSE_CURLY_BRACE); + + $partialObjectExpression = new AST\PartialObjectExpression($identificationVariable, $partialFieldSet); + + // Defer PartialObjectExpression validation + $this->_deferredPartialObjectExpressions[] = array( + 'expression' => $partialObjectExpression, + 'nestingLevel' => $this->_nestingLevel, + 'token' => $this->_lexer->token, + ); + + return $partialObjectExpression; + } + + /** + * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinAssociationPathExpression + * ["AS"] AliasIdentificationVariable ["WITH" ConditionalExpression] + * + * @return \Doctrine\ORM\Query\AST\Join + */ + public function Join() + { + // Check Join type + $joinType = AST\Join::JOIN_TYPE_INNER; + + switch (true) { + case ($this->_lexer->isNextToken(Lexer::T_LEFT)): + $this->match(Lexer::T_LEFT); + + $joinType = AST\Join::JOIN_TYPE_LEFT; + + // Possible LEFT OUTER join + if ($this->_lexer->isNextToken(Lexer::T_OUTER)) { + $this->match(Lexer::T_OUTER); + + $joinType = AST\Join::JOIN_TYPE_LEFTOUTER; + } + break; + + case ($this->_lexer->isNextToken(Lexer::T_INNER)): + $this->match(Lexer::T_INNER); + break; + + default: + // Do nothing + } + + $this->match(Lexer::T_JOIN); + + $joinPathExpression = $this->JoinAssociationPathExpression(); + + if ($this->_lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $token = $this->_lexer->lookahead; + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + + // Verify that the association exists. + $parentClass = $this->_queryComponents[$joinPathExpression->identificationVariable]['metadata']; + $assocField = $joinPathExpression->associationField; + + if ( ! $parentClass->hasAssociation($assocField)) { + $this->semanticalError( + "Class " . $parentClass->name . " has no association named '$assocField'." + ); + } + + $targetClassName = $parentClass->associationMappings[$assocField]['targetEntity']; + + // Building queryComponent + $joinQueryComponent = array( + 'metadata' => $this->_em->getClassMetadata($targetClassName), + 'parent' => $joinPathExpression->identificationVariable, + 'relation' => $parentClass->getAssociationMapping($assocField), + 'map' => null, + 'nestingLevel' => $this->_nestingLevel, + 'token' => $token + ); + + $this->_queryComponents[$aliasIdentificationVariable] = $joinQueryComponent; + + // Create AST node + $join = new AST\Join($joinType, $joinPathExpression, $aliasIdentificationVariable); + + // Check for ad-hoc Join conditions + if ($this->_lexer->isNextToken(Lexer::T_WITH)) { + $this->match(Lexer::T_WITH); + + $join->conditionalExpression = $this->ConditionalExpression(); + } + + return $join; + } + + /** + * IndexBy ::= "INDEX" "BY" StateFieldPathExpression + * + * @return \Doctrine\ORM\Query\AST\IndexBy + */ + public function IndexBy() + { + $this->match(Lexer::T_INDEX); + $this->match(Lexer::T_BY); + $pathExpr = $this->StateFieldPathExpression(); + + // Add the INDEX BY info to the query component + $this->_queryComponents[$pathExpr->identificationVariable]['map'] = $pathExpr->field; + + return new AST\IndexBy($pathExpr); + } + + /** + * ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary | + * StateFieldPathExpression | BooleanPrimary | CaseExpression | + * InstanceOfExpression + * + * @return mixed One of the possible expressions or subexpressions. + */ + public function ScalarExpression() + { + $lookahead = $this->_lexer->lookahead['type']; + + switch ($lookahead) { + case Lexer::T_IDENTIFIER: + $this->_lexer->peek(); // lookahead => '.' + $this->_lexer->peek(); // lookahead => token after '.' + $peek = $this->_lexer->peek(); // lookahead => token after the token after the '.' + $this->_lexer->resetPeek(); + + if ($this->_isMathOperator($peek)) { + return $this->SimpleArithmeticExpression(); + } + + return $this->StateFieldPathExpression(); + + case Lexer::T_INTEGER: + case Lexer::T_FLOAT: + return $this->SimpleArithmeticExpression(); + + case Lexer::T_STRING: + return $this->StringPrimary(); + + case Lexer::T_TRUE: + case Lexer::T_FALSE: + $this->match($lookahead); + + return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']); + + case Lexer::T_INPUT_PARAMETER: + return $this->InputParameter(); + + case Lexer::T_CASE: + case Lexer::T_COALESCE: + case Lexer::T_NULLIF: + // Since NULLIF and COALESCE can be identified as a function, + // we need to check if before check for FunctionDeclaration + return $this->CaseExpression(); + + default: + if ( ! ($this->_isFunction() || $this->_isAggregateFunction($lookahead))) { + $this->syntaxError(); + } + + // We may be in an ArithmeticExpression (find the matching ")" and inspect for Math operator) + $this->_lexer->peek(); // "(" + $peek = $this->_peekBeyondClosingParenthesis(); + + if ($this->_isMathOperator($peek)) { + return $this->SimpleArithmeticExpression(); + } + + if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { + return $this->AggregateExpression(); + } + + return $this->FunctionDeclaration(); + } + } + + /** + * CaseExpression ::= GeneralCaseExpression | SimpleCaseExpression | CoalesceExpression | NullifExpression + * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" + * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression + * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" + * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator + * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression + * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" + * NullifExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" + * + * @return mixed One of the possible expressions or subexpressions. + */ + public function CaseExpression() + { + $lookahead = $this->_lexer->lookahead['type']; + + switch ($lookahead) { + case Lexer::T_NULLIF: + return $this->NullIfExpression(); + + case Lexer::T_COALESCE: + return $this->CoalesceExpression(); + + case Lexer::T_CASE: + $this->_lexer->resetPeek(); + $peek = $this->_lexer->peek(); + + if ($peek['type'] === Lexer::T_WHEN) { + return $this->GeneralCaseExpression(); + } + + return $this->SimpleCaseExpression(); + + default: + // Do nothing + break; + } + + $this->syntaxError(); + } + + /** + * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" + * + * @return \Doctrine\ORM\Query\AST\CoalesceExpression + */ + public function CoalesceExpression() + { + $this->match(Lexer::T_COALESCE); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + // Process ScalarExpressions (1..N) + $scalarExpressions = array(); + $scalarExpressions[] = $this->ScalarExpression(); + + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $scalarExpressions[] = $this->ScalarExpression(); + } + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return new AST\CoalesceExpression($scalarExpressions); + } + + /** + * NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" + * + * @return \Doctrine\ORM\Query\AST\NullIfExpression + */ + public function NullIfExpression() + { + $this->match(Lexer::T_NULLIF); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $firstExpression = $this->ScalarExpression(); + $this->match(Lexer::T_COMMA); + $secondExpression = $this->ScalarExpression(); + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return new AST\NullIfExpression($firstExpression, $secondExpression); + } + + /** + * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" + * + * @return \Doctrine\ORM\Query\AST\GeneralExpression + */ + public function GeneralCaseExpression() + { + $this->match(Lexer::T_CASE); + + // Process WhenClause (1..N) + $whenClauses = array(); + + do { + $whenClauses[] = $this->WhenClause(); + } while ($this->_lexer->isNextToken(Lexer::T_WHEN)); + + $this->match(Lexer::T_ELSE); + $scalarExpression = $this->ScalarExpression(); + $this->match(Lexer::T_END); + + return new AST\GeneralCaseExpression($whenClauses, $scalarExpression); + } + + /** + * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" + * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator + */ + public function SimpleCaseExpression() + { + $this->match(Lexer::T_CASE); + $caseOperand = $this->StateFieldPathExpression(); + + // Process SimpleWhenClause (1..N) + $simpleWhenClauses = array(); + + do { + $simpleWhenClauses[] = $this->SimpleWhenClause(); + } while ($this->_lexer->isNextToken(Lexer::T_WHEN)); + + $this->match(Lexer::T_ELSE); + $scalarExpression = $this->ScalarExpression(); + $this->match(Lexer::T_END); + + return new AST\SimpleCaseExpression($caseOperand, $simpleWhenClauses, $scalarExpression); + } + + /** + * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression + * + * @return \Doctrine\ORM\Query\AST\WhenExpression + */ + public function WhenClause() + { + $this->match(Lexer::T_WHEN); + $conditionalExpression = $this->ConditionalExpression(); + $this->match(Lexer::T_THEN); + + return new AST\WhenClause($conditionalExpression, $this->ScalarExpression()); + } + + /** + * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression + * + * @return \Doctrine\ORM\Query\AST\SimpleWhenExpression + */ + public function SimpleWhenClause() + { + $this->match(Lexer::T_WHEN); + $conditionalExpression = $this->ScalarExpression(); + $this->match(Lexer::T_THEN); + + return new AST\SimpleWhenClause($conditionalExpression, $this->ScalarExpression()); + } + + /** + * SelectExpression ::= ( + * IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | + * PartialObjectExpression | "(" Subselect ")" | CaseExpression + * ) [["AS"] ["HIDDEN"] AliasResultVariable] + * + * @return \Doctrine\ORM\Query\AST\SelectExpression + */ + public function SelectExpression() + { + $expression = null; + $identVariable = null; + $peek = $this->_lexer->glimpse(); + $lookaheadType = $this->_lexer->lookahead['type']; + + switch (true) { + // ScalarExpression (u.name) + case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_DOT): + $expression = $this->ScalarExpression(); + break; + + // IdentificationVariable (u) + case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] !== Lexer::T_OPEN_PARENTHESIS): + $expression = $identVariable = $this->IdentificationVariable(); + break; + + // CaseExpression (CASE ... or NULLIF(...) or COALESCE(...)) + case ($lookaheadType === Lexer::T_CASE): + case ($lookaheadType === Lexer::T_COALESCE): + case ($lookaheadType === Lexer::T_NULLIF): + $expression = $this->CaseExpression(); + break; + + // DQL Function (SUM(u.value) or SUM(u.value) + 1) + case ($this->_isFunction()): + $this->_lexer->peek(); // "(" + + switch (true) { + case ($this->_isMathOperator($this->_peekBeyondClosingParenthesis())): + // SUM(u.id) + COUNT(u.id) + $expression = $this->ScalarExpression(); + break; + + case ($this->_isAggregateFunction($lookaheadType)): + // COUNT(u.id) + $expression = $this->AggregateExpression(); + break; + + default: + // IDENTITY(u) + $expression = $this->FunctionDeclaration(); + break; + } + + break; + + // PartialObjectExpression (PARTIAL u.{id, name}) + case ($lookaheadType === Lexer::T_PARTIAL): + $expression = $this->PartialObjectExpression(); + $identVariable = $expression->identificationVariable; + break; + + // Subselect + case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT): + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expression = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + break; + + // Shortcut: ScalarExpression => SimpleArithmeticExpression + case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS): + case ($lookaheadType === Lexer::T_INTEGER): + case ($lookaheadType === Lexer::T_STRING): + case ($lookaheadType === Lexer::T_FLOAT): + // SimpleArithmeticExpression : (- u.value ) or ( + u.value ) + case ($lookaheadType === Lexer::T_MINUS): + case ($lookaheadType === Lexer::T_PLUS): + $expression = $this->SimpleArithmeticExpression(); + break; + + default: + $this->syntaxError( + 'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression', + $this->_lexer->lookahead + ); + } + + // [["AS"] ["HIDDEN"] AliasResultVariable] + + if ($this->_lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $hiddenAliasResultVariable = false; + + if ($this->_lexer->isNextToken(Lexer::T_HIDDEN)) { + $this->match(Lexer::T_HIDDEN); + + $hiddenAliasResultVariable = true; + } + + $aliasResultVariable = null; + + if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { + $token = $this->_lexer->lookahead; + $aliasResultVariable = $this->AliasResultVariable(); + + // Include AliasResultVariable in query components. + $this->_queryComponents[$aliasResultVariable] = array( + 'resultVariable' => $expression, + 'nestingLevel' => $this->_nestingLevel, + 'token' => $token, + ); + } + + // AST + + $expr = new AST\SelectExpression($expression, $aliasResultVariable, $hiddenAliasResultVariable); + + if ($identVariable) { + $this->_identVariableExpressions[$identVariable] = $expr; + } + + return $expr; + } + + /** + * SimpleSelectExpression ::= + * StateFieldPathExpression | IdentificationVariable | + * ((AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable]) + * + * @return \Doctrine\ORM\Query\AST\SimpleSelectExpression + */ + public function SimpleSelectExpression() + { + $peek = $this->_lexer->glimpse(); + + switch ($this->_lexer->lookahead['type']) { + case Lexer::T_IDENTIFIER: + switch (true) { + case ($peek['type'] === Lexer::T_DOT): + $expression = $this->StateFieldPathExpression(); + + return new AST\SimpleSelectExpression($expression); + + case ($peek['type'] !== Lexer::T_OPEN_PARENTHESIS): + $expression = $this->IdentificationVariable(); + + return new AST\SimpleSelectExpression($expression); + + default: + // Do nothing + } + break; + + case Lexer::T_OPEN_PARENTHESIS: + if ($peek['type'] !== Lexer::T_SELECT) { + // Shortcut: ScalarExpression => SimpleArithmeticExpression + $expression = $this->SimpleArithmeticExpression(); + + return new AST\SimpleSelectExpression($expression); + } + + // Subselect + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expression = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return new AST\SimpleSelectExpression($expression); + + default: + // Do nothing + } + + $this->_lexer->peek(); + + $expression = $this->ScalarExpression(); + $expr = new AST\SimpleSelectExpression($expression); + + if ($this->_lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { + $token = $this->_lexer->lookahead; + $resultVariable = $this->AliasResultVariable(); + $expr->fieldIdentificationVariable = $resultVariable; + + // Include AliasResultVariable in query components. + $this->_queryComponents[$resultVariable] = array( + 'resultvariable' => $expr, + 'nestingLevel' => $this->_nestingLevel, + 'token' => $token, + ); + } + + return $expr; + } + + /** + * ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}* + * + * @return \Doctrine\ORM\Query\AST\ConditionalExpression + */ + public function ConditionalExpression() + { + $conditionalTerms = array(); + $conditionalTerms[] = $this->ConditionalTerm(); + + while ($this->_lexer->isNextToken(Lexer::T_OR)) { + $this->match(Lexer::T_OR); + + $conditionalTerms[] = $this->ConditionalTerm(); + } + + // Phase 1 AST optimization: Prevent AST\ConditionalExpression + // if only one AST\ConditionalTerm is defined + if (count($conditionalTerms) == 1) { + return $conditionalTerms[0]; + } + + return new AST\ConditionalExpression($conditionalTerms); + } + + /** + * ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}* + * + * @return \Doctrine\ORM\Query\AST\ConditionalTerm + */ + public function ConditionalTerm() + { + $conditionalFactors = array(); + $conditionalFactors[] = $this->ConditionalFactor(); + + while ($this->_lexer->isNextToken(Lexer::T_AND)) { + $this->match(Lexer::T_AND); + + $conditionalFactors[] = $this->ConditionalFactor(); + } + + // Phase 1 AST optimization: Prevent AST\ConditionalTerm + // if only one AST\ConditionalFactor is defined + if (count($conditionalFactors) == 1) { + return $conditionalFactors[0]; + } + + return new AST\ConditionalTerm($conditionalFactors); + } + + /** + * ConditionalFactor ::= ["NOT"] ConditionalPrimary + * + * @return \Doctrine\ORM\Query\AST\ConditionalFactor + */ + public function ConditionalFactor() + { + $not = false; + + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + + $not = true; + } + + $conditionalPrimary = $this->ConditionalPrimary(); + + // Phase 1 AST optimization: Prevent AST\ConditionalFactor + // if only one AST\ConditionalPrimary is defined + if ( ! $not) { + return $conditionalPrimary; + } + + $conditionalFactor = new AST\ConditionalFactor($conditionalPrimary); + $conditionalFactor->not = $not; + + return $conditionalFactor; + } + + /** + * ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")" + * + * @return \Doctrine\ORM\Query\AST\ConditionalPrimary + */ + public function ConditionalPrimary() + { + $condPrimary = new AST\ConditionalPrimary; + + if ( ! $this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression(); + + return $condPrimary; + } + + // Peek beyond the matching closing paranthesis ')' + $peek = $this->_peekBeyondClosingParenthesis(); + + if (in_array($peek['value'], array("=", "<", "<=", "<>", ">", ">=", "!=")) || + in_array($peek['type'], array(Lexer::T_NOT, Lexer::T_BETWEEN, Lexer::T_LIKE, Lexer::T_IN, Lexer::T_IS, Lexer::T_EXISTS)) || + $this->_isMathOperator($peek)) { + $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression(); + + return $condPrimary; + } + + $this->match(Lexer::T_OPEN_PARENTHESIS); + $condPrimary->conditionalExpression = $this->ConditionalExpression(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $condPrimary; + } + + /** + * SimpleConditionalExpression ::= + * ComparisonExpression | BetweenExpression | LikeExpression | + * InExpression | NullComparisonExpression | ExistsExpression | + * EmptyCollectionComparisonExpression | CollectionMemberExpression | + * InstanceOfExpression + */ + public function SimpleConditionalExpression() + { + $token = $this->_lexer->lookahead; + + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + $token = $this->_lexer->glimpse(); + } + + if ($token['type'] === Lexer::T_EXISTS) { + return $this->ExistsExpression(); + } + + $peek = $this->_lexer->glimpse(); + + if ($token['type'] === Lexer::T_IDENTIFIER || $token['type'] === Lexer::T_INPUT_PARAMETER) { + if ($peek['value'] == '(') { + // Peek beyond the matching closing paranthesis ')' + $this->_lexer->peek(); + $token = $this->_peekBeyondClosingParenthesis(); + } else { + // Peek beyond the PathExpression (or InputParameter) + $peek = $this->_lexer->peek(); + + while ($peek['value'] === '.') { + $this->_lexer->peek(); + $peek = $this->_lexer->peek(); + } + + // Also peek beyond a NOT if there is one + if ($peek['type'] === Lexer::T_NOT) { + $peek = $this->_lexer->peek(); + } + + $token = $peek; + + // We need to go even further in case of IS (differenciate between NULL and EMPTY) + $lookahead = $this->_lexer->peek(); + + // Also peek beyond a NOT if there is one + if ($lookahead['type'] === Lexer::T_NOT) { + $lookahead = $this->_lexer->peek(); + } + + $this->_lexer->resetPeek(); + } + } + + switch ($token['type']) { + case Lexer::T_BETWEEN: + return $this->BetweenExpression(); + case Lexer::T_LIKE: + return $this->LikeExpression(); + case Lexer::T_IN: + return $this->InExpression(); + case Lexer::T_INSTANCE: + return $this->InstanceOfExpression(); + case Lexer::T_IS: + if ($lookahead['type'] == Lexer::T_NULL) { + return $this->NullComparisonExpression(); + } + return $this->EmptyCollectionComparisonExpression(); + case Lexer::T_MEMBER: + return $this->CollectionMemberExpression(); + default: + return $this->ComparisonExpression(); + } + } + + /** + * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" + * + * @return \Doctrine\ORM\Query\AST\EmptyCollectionComparisonExpression + */ + public function EmptyCollectionComparisonExpression() + { + $emptyColletionCompExpr = new AST\EmptyCollectionComparisonExpression( + $this->CollectionValuedPathExpression() + ); + $this->match(Lexer::T_IS); + + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $emptyColletionCompExpr->not = true; + } + + $this->match(Lexer::T_EMPTY); + + return $emptyColletionCompExpr; + } + + /** + * CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression + * + * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression + * SimpleEntityExpression ::= IdentificationVariable | InputParameter + * + * @return \Doctrine\ORM\Query\AST\CollectionMemberExpression + */ + public function CollectionMemberExpression() + { + $not = false; + $entityExpr = $this->EntityExpression(); + + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + + $not = true; + } + + $this->match(Lexer::T_MEMBER); + + if ($this->_lexer->isNextToken(Lexer::T_OF)) { + $this->match(Lexer::T_OF); + } + + $collMemberExpr = new AST\CollectionMemberExpression( + $entityExpr, $this->CollectionValuedPathExpression() + ); + $collMemberExpr->not = $not; + + return $collMemberExpr; + } + + /** + * Literal ::= string | char | integer | float | boolean + * + * @return string + */ + public function Literal() + { + switch ($this->_lexer->lookahead['type']) { + case Lexer::T_STRING: + $this->match(Lexer::T_STRING); + return new AST\Literal(AST\Literal::STRING, $this->_lexer->token['value']); + + case Lexer::T_INTEGER: + case Lexer::T_FLOAT: + $this->match( + $this->_lexer->isNextToken(Lexer::T_INTEGER) ? Lexer::T_INTEGER : Lexer::T_FLOAT + ); + return new AST\Literal(AST\Literal::NUMERIC, $this->_lexer->token['value']); + + case Lexer::T_TRUE: + case Lexer::T_FALSE: + $this->match( + $this->_lexer->isNextToken(Lexer::T_TRUE) ? Lexer::T_TRUE : Lexer::T_FALSE + ); + return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']); + + default: + $this->syntaxError('Literal'); + } + } + + /** + * InParameter ::= Literal | InputParameter + * + * @return string | \Doctrine\ORM\Query\AST\InputParameter + */ + public function InParameter() + { + if ($this->_lexer->lookahead['type'] == Lexer::T_INPUT_PARAMETER) { + return $this->InputParameter(); + } + + return $this->Literal(); + } + + /** + * InputParameter ::= PositionalParameter | NamedParameter + * + * @return \Doctrine\ORM\Query\AST\InputParameter + */ + public function InputParameter() + { + $this->match(Lexer::T_INPUT_PARAMETER); + + return new AST\InputParameter($this->_lexer->token['value']); + } + + /** + * ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")" + * + * @return \Doctrine\ORM\Query\AST\ArithmeticExpression + */ + public function ArithmeticExpression() + { + $expr = new AST\ArithmeticExpression; + + if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $peek = $this->_lexer->glimpse(); + + if ($peek['type'] === Lexer::T_SELECT) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expr->subselect = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $expr; + } + } + + $expr->simpleArithmeticExpression = $this->SimpleArithmeticExpression(); + + return $expr; + } + + /** + * SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}* + * + * @return \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public function SimpleArithmeticExpression() + { + $terms = array(); + $terms[] = $this->ArithmeticTerm(); + + while (($isPlus = $this->_lexer->isNextToken(Lexer::T_PLUS)) || $this->_lexer->isNextToken(Lexer::T_MINUS)) { + $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS); + + $terms[] = $this->_lexer->token['value']; + $terms[] = $this->ArithmeticTerm(); + } + + // Phase 1 AST optimization: Prevent AST\SimpleArithmeticExpression + // if only one AST\ArithmeticTerm is defined + if (count($terms) == 1) { + return $terms[0]; + } + + return new AST\SimpleArithmeticExpression($terms); + } + + /** + * ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}* + * + * @return \Doctrine\ORM\Query\AST\ArithmeticTerm + */ + public function ArithmeticTerm() + { + $factors = array(); + $factors[] = $this->ArithmeticFactor(); + + while (($isMult = $this->_lexer->isNextToken(Lexer::T_MULTIPLY)) || $this->_lexer->isNextToken(Lexer::T_DIVIDE)) { + $this->match(($isMult) ? Lexer::T_MULTIPLY : Lexer::T_DIVIDE); + + $factors[] = $this->_lexer->token['value']; + $factors[] = $this->ArithmeticFactor(); + } + + // Phase 1 AST optimization: Prevent AST\ArithmeticTerm + // if only one AST\ArithmeticFactor is defined + if (count($factors) == 1) { + return $factors[0]; + } + + return new AST\ArithmeticTerm($factors); + } + + /** + * ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary + * + * @return \Doctrine\ORM\Query\AST\ArithmeticFactor + */ + public function ArithmeticFactor() + { + $sign = null; + + if (($isPlus = $this->_lexer->isNextToken(Lexer::T_PLUS)) || $this->_lexer->isNextToken(Lexer::T_MINUS)) { + $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS); + $sign = $isPlus; + } + + $primary = $this->ArithmeticPrimary(); + + // Phase 1 AST optimization: Prevent AST\ArithmeticFactor + // if only one AST\ArithmeticPrimary is defined + if ($sign === null) { + return $primary; + } + + return new AST\ArithmeticFactor($primary, $sign); + } + + /** + * ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")" + * | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings + * | FunctionsReturningDatetime | IdentificationVariable | ResultVariable | CaseExpression + */ + public function ArithmeticPrimary() + { + if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expr = $this->SimpleArithmeticExpression(); + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $expr; + } + + switch ($this->_lexer->lookahead['type']) { + case Lexer::T_COALESCE: + case Lexer::T_NULLIF: + case Lexer::T_CASE: + return $this->CaseExpression(); + + case Lexer::T_IDENTIFIER: + $peek = $this->_lexer->glimpse(); + + if ($peek['value'] == '(') { + return $this->FunctionDeclaration(); + } + + if ($peek['value'] == '.') { + return $this->SingleValuedPathExpression(); + } + + if (isset($this->_queryComponents[$this->_lexer->lookahead['value']]['resultVariable'])) { + return $this->ResultVariable(); + } + + return $this->StateFieldPathExpression(); + + case Lexer::T_INPUT_PARAMETER: + return $this->InputParameter(); + + default: + $peek = $this->_lexer->glimpse(); + + if ($peek['value'] == '(') { + if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { + return $this->AggregateExpression(); + } + + return $this->FunctionDeclaration(); + } + + return $this->Literal(); + } + } + + /** + * StringExpression ::= StringPrimary | "(" Subselect ")" + * + * @return \Doctrine\ORM\Query\AST\StringPrimary | + * \Doctrine]ORM\Query\AST\Subselect + */ + public function StringExpression() + { + if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $peek = $this->_lexer->glimpse(); + + if ($peek['type'] === Lexer::T_SELECT) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expr = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $expr; + } + } + + return $this->StringPrimary(); + } + + /** + * StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression + */ + public function StringPrimary() + { + $lookaheadType = $this->_lexer->lookahead['type']; + + switch ($lookaheadType) { + case Lexer::T_IDENTIFIER: + $peek = $this->_lexer->glimpse(); + + if ($peek['value'] == '.') { + return $this->StateFieldPathExpression(); + } + + if ($peek['value'] == '(') { + // do NOT directly go to FunctionsReturningString() because it doesnt check for custom functions. + return $this->FunctionDeclaration(); + } + + $this->syntaxError("'.' or '('"); + break; + + case Lexer::T_STRING: + $this->match(Lexer::T_STRING); + + return $this->_lexer->token['value']; + + case Lexer::T_INPUT_PARAMETER: + return $this->InputParameter(); + + case Lexer::T_CASE: + case Lexer::T_COALESCE: + case Lexer::T_NULLIF: + return $this->CaseExpression(); + + default: + if ($this->_isAggregateFunction($lookaheadType)) { + return $this->AggregateExpression(); + } + } + + $this->syntaxError( + 'StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression' + ); + } + + /** + * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression + * + * @return \Doctrine\ORM\Query\AST\SingleValuedAssociationPathExpression | + * \Doctrine\ORM\Query\AST\SimpleEntityExpression + */ + public function EntityExpression() + { + $glimpse = $this->_lexer->glimpse(); + + if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER) && $glimpse['value'] === '.') { + return $this->SingleValuedAssociationPathExpression(); + } + + return $this->SimpleEntityExpression(); + } + + /** + * SimpleEntityExpression ::= IdentificationVariable | InputParameter + * + * @return string | \Doctrine\ORM\Query\AST\InputParameter + */ + public function SimpleEntityExpression() + { + if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + return $this->InputParameter(); + } + + return $this->StateFieldPathExpression(); + } + + /** + * AggregateExpression ::= + * ("AVG" | "MAX" | "MIN" | "SUM") "(" ["DISTINCT"] StateFieldPathExpression ")" | + * "COUNT" "(" ["DISTINCT"] (IdentificationVariable | SingleValuedPathExpression) ")" + * + * @return \Doctrine\ORM\Query\AST\AggregateExpression + */ + public function AggregateExpression() + { + $lookaheadType = $this->_lexer->lookahead['type']; + $isDistinct = false; + + if ( ! in_array($lookaheadType, array(Lexer::T_COUNT, Lexer::T_AVG, Lexer::T_MAX, Lexer::T_MIN, Lexer::T_SUM))) { + $this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT'); + } + + $this->match($lookaheadType); + $functionName = $this->_lexer->token['value']; + $this->match(Lexer::T_OPEN_PARENTHESIS); + + if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) { + $this->match(Lexer::T_DISTINCT); + $isDistinct = true; + } + + $pathExp = ($lookaheadType === Lexer::T_COUNT) + ? $this->SingleValuedPathExpression() + : $this->SimpleArithmeticExpression(); + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return new AST\AggregateExpression($functionName, $pathExp, $isDistinct); + } + + /** + * QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")" + * + * @return \Doctrine\ORM\Query\AST\QuantifiedExpression + */ + public function QuantifiedExpression() + { + $lookaheadType = $this->_lexer->lookahead['type']; + $value = $this->_lexer->lookahead['value']; + + if ( ! in_array($lookaheadType, array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME))) { + $this->syntaxError('ALL, ANY or SOME'); + } + + $this->match($lookaheadType); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $qExpr = new AST\QuantifiedExpression($this->Subselect()); + $qExpr->type = $value; + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $qExpr; + } + + /** + * BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression + * + * @return \Doctrine\ORM\Query\AST\BetweenExpression + */ + public function BetweenExpression() + { + $not = false; + $arithExpr1 = $this->ArithmeticExpression(); + + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $not = true; + } + + $this->match(Lexer::T_BETWEEN); + $arithExpr2 = $this->ArithmeticExpression(); + $this->match(Lexer::T_AND); + $arithExpr3 = $this->ArithmeticExpression(); + + $betweenExpr = new AST\BetweenExpression($arithExpr1, $arithExpr2, $arithExpr3); + $betweenExpr->not = $not; + + return $betweenExpr; + } + + /** + * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) + * + * @return \Doctrine\ORM\Query\AST\ComparisonExpression + */ + public function ComparisonExpression() + { + $peek = $this->_lexer->glimpse(); + + $leftExpr = $this->ArithmeticExpression(); + $operator = $this->ComparisonOperator(); + $rightExpr = ($this->_isNextAllAnySome()) + ? $this->QuantifiedExpression() + : $this->ArithmeticExpression(); + + return new AST\ComparisonExpression($leftExpr, $operator, $rightExpr); + } + + /** + * InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")" + * + * @return \Doctrine\ORM\Query\AST\InExpression + */ + public function InExpression() + { + $inExpression = new AST\InExpression($this->ArithmeticExpression()); + + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $inExpression->not = true; + } + + $this->match(Lexer::T_IN); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + if ($this->_lexer->isNextToken(Lexer::T_SELECT)) { + $inExpression->subselect = $this->Subselect(); + } else { + $literals = array(); + $literals[] = $this->InParameter(); + + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + $literals[] = $this->InParameter(); + } + + $inExpression->literals = $literals; + } + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $inExpression; + } + + /** + * InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")") + * + * @return \Doctrine\ORM\Query\AST\InstanceOfExpression + */ + public function InstanceOfExpression() + { + $instanceOfExpression = new AST\InstanceOfExpression($this->IdentificationVariable()); + + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $instanceOfExpression->not = true; + } + + $this->match(Lexer::T_INSTANCE); + $this->match(Lexer::T_OF); + + $exprValues = array(); + + if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $exprValues[] = $this->InstanceOfParameter(); + + while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $exprValues[] = $this->InstanceOfParameter(); + } + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + $instanceOfExpression->value = $exprValues; + + return $instanceOfExpression; + } + + $exprValues[] = $this->InstanceOfParameter(); + + $instanceOfExpression->value = $exprValues; + + return $instanceOfExpression; + } + + /** + * InstanceOfParameter ::= AbstractSchemaName | InputParameter + * + * @return mixed + */ + public function InstanceOfParameter() + { + if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + $this->match(Lexer::T_INPUT_PARAMETER); + + return new AST\InputParameter($this->_lexer->token['value']); + } + + return $this->AliasIdentificationVariable(); + } + + /** + * LikeExpression ::= StringExpression ["NOT"] "LIKE" (string | input_parameter) ["ESCAPE" char] + * + * @return \Doctrine\ORM\Query\AST\LikeExpression + */ + public function LikeExpression() + { + $stringExpr = $this->StringExpression(); + $not = false; + + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $not = true; + } + + $this->match(Lexer::T_LIKE); + + if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + $this->match(Lexer::T_INPUT_PARAMETER); + $stringPattern = new AST\InputParameter($this->_lexer->token['value']); + } else { + $this->match(Lexer::T_STRING); + $stringPattern = $this->_lexer->token['value']; + } + + $escapeChar = null; + + if ($this->_lexer->lookahead['type'] === Lexer::T_ESCAPE) { + $this->match(Lexer::T_ESCAPE); + $this->match(Lexer::T_STRING); + $escapeChar = $this->_lexer->token['value']; + } + + $likeExpr = new AST\LikeExpression($stringExpr, $stringPattern, $escapeChar); + $likeExpr->not = $not; + + return $likeExpr; + } + + /** + * NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL" + * + * @return \Doctrine\ORM\Query\AST\NullComparisonExpression + */ + public function NullComparisonExpression() + { + if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + $this->match(Lexer::T_INPUT_PARAMETER); + $expr = new AST\InputParameter($this->_lexer->token['value']); + } else { + $expr = $this->SingleValuedPathExpression(); + } + + $nullCompExpr = new AST\NullComparisonExpression($expr); + $this->match(Lexer::T_IS); + + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $nullCompExpr->not = true; + } + + $this->match(Lexer::T_NULL); + + return $nullCompExpr; + } + + /** + * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" + * + * @return \Doctrine\ORM\Query\AST\ExistsExpression + */ + public function ExistsExpression() + { + $not = false; + + if ($this->_lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $not = true; + } + + $this->match(Lexer::T_EXISTS); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $existsExpression = new AST\ExistsExpression($this->Subselect()); + $existsExpression->not = $not; + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $existsExpression; + } + + /** + * ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!=" + * + * @return string + */ + public function ComparisonOperator() + { + switch ($this->_lexer->lookahead['value']) { + case '=': + $this->match(Lexer::T_EQUALS); + + return '='; + + case '<': + $this->match(Lexer::T_LOWER_THAN); + $operator = '<'; + + if ($this->_lexer->isNextToken(Lexer::T_EQUALS)) { + $this->match(Lexer::T_EQUALS); + $operator .= '='; + } else if ($this->_lexer->isNextToken(Lexer::T_GREATER_THAN)) { + $this->match(Lexer::T_GREATER_THAN); + $operator .= '>'; + } + + return $operator; + + case '>': + $this->match(Lexer::T_GREATER_THAN); + $operator = '>'; + + if ($this->_lexer->isNextToken(Lexer::T_EQUALS)) { + $this->match(Lexer::T_EQUALS); + $operator .= '='; + } + + return $operator; + + case '!': + $this->match(Lexer::T_NEGATE); + $this->match(Lexer::T_EQUALS); + + return '<>'; + + default: + $this->syntaxError('=, <, <=, <>, >, >=, !='); + } + } + + /** + * FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDatetime + */ + public function FunctionDeclaration() + { + $token = $this->_lexer->lookahead; + $funcName = strtolower($token['value']); + + // Check for built-in functions first! + switch (true) { + case (isset(self::$_STRING_FUNCTIONS[$funcName])): + return $this->FunctionsReturningStrings(); + + case (isset(self::$_NUMERIC_FUNCTIONS[$funcName])): + return $this->FunctionsReturningNumerics(); + + case (isset(self::$_DATETIME_FUNCTIONS[$funcName])): + return $this->FunctionsReturningDatetime(); + + default: + return $this->CustomFunctionDeclaration(); + } + } + + /** + * Helper function for FunctionDeclaration grammar rule + */ + private function CustomFunctionDeclaration() + { + $token = $this->_lexer->lookahead; + $funcName = strtolower($token['value']); + + // Check for custom functions afterwards + $config = $this->_em->getConfiguration(); + + switch (true) { + case ($config->getCustomStringFunction($funcName) !== null): + return $this->CustomFunctionsReturningStrings(); + + case ($config->getCustomNumericFunction($funcName) !== null): + return $this->CustomFunctionsReturningNumerics(); + + case ($config->getCustomDatetimeFunction($funcName) !== null): + return $this->CustomFunctionsReturningDatetime(); + + default: + $this->syntaxError('known function', $token); + } + } + + /** + * FunctionsReturningNumerics ::= + * "LENGTH" "(" StringPrimary ")" | + * "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" | + * "ABS" "(" SimpleArithmeticExpression ")" | + * "SQRT" "(" SimpleArithmeticExpression ")" | + * "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | + * "SIZE" "(" CollectionValuedPathExpression ")" + */ + public function FunctionsReturningNumerics() + { + $funcNameLower = strtolower($this->_lexer->lookahead['value']); + $funcClass = self::$_NUMERIC_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + public function CustomFunctionsReturningNumerics() + { + // getCustomNumericFunction is case-insensitive + $funcName = strtolower($this->_lexer->lookahead['value']); + $funcClass = $this->_em->getConfiguration()->getCustomNumericFunction($funcName); + + $function = new $funcClass($funcName); + $function->parse($this); + + return $function; + } + + /** + * FunctionsReturningDateTime ::= "CURRENT_DATE" | "CURRENT_TIME" | "CURRENT_TIMESTAMP" + */ + public function FunctionsReturningDatetime() + { + $funcNameLower = strtolower($this->_lexer->lookahead['value']); + $funcClass = self::$_DATETIME_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + public function CustomFunctionsReturningDatetime() + { + // getCustomDatetimeFunction is case-insensitive + $funcName = $this->_lexer->lookahead['value']; + $funcClass = $this->_em->getConfiguration()->getCustomDatetimeFunction($funcName); + + $function = new $funcClass($funcName); + $function->parse($this); + + return $function; + } + + /** + * FunctionsReturningStrings ::= + * "CONCAT" "(" StringPrimary "," StringPrimary ")" | + * "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | + * "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" | + * "LOWER" "(" StringPrimary ")" | + * "UPPER" "(" StringPrimary ")" + */ + public function FunctionsReturningStrings() + { + $funcNameLower = strtolower($this->_lexer->lookahead['value']); + $funcClass = self::$_STRING_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + public function CustomFunctionsReturningStrings() + { + // getCustomStringFunction is case-insensitive + $funcName = $this->_lexer->lookahead['value']; + $funcClass = $this->_em->getConfiguration()->getCustomStringFunction($funcName); + + $function = new $funcClass($funcName); + $function->parse($this); + + return $function; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParserResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParserResult.php new file mode 100644 index 0000000..3e938a9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParserResult.php @@ -0,0 +1,141 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Encapsulates the resulting components from a DQL query parsing process that + * can be serialized. + * + * @author Guilherme Blanco + * @author Janne Vanhala + * @author Roman Borschel + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + */ +class ParserResult +{ + /** + * The SQL executor used for executing the SQL. + * + * @var \Doctrine\ORM\Query\Exec\AbstractSqlExecutor + */ + private $_sqlExecutor; + + /** + * The ResultSetMapping that describes how to map the SQL result set. + * + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + private $_resultSetMapping; + + /** + * The mappings of DQL parameter names/positions to SQL parameter positions. + * + * @var array + */ + private $_parameterMappings = array(); + + /** + * Initializes a new instance of the ParserResult class. + * The new instance is initialized with an empty ResultSetMapping. + */ + public function __construct() + { + $this->_resultSetMapping = new ResultSetMapping; + } + + /** + * Gets the ResultSetMapping for the parsed query. + * + * @return ResultSetMapping The result set mapping of the parsed query or NULL + * if the query is not a SELECT query. + */ + public function getResultSetMapping() + { + return $this->_resultSetMapping; + } + + /** + * Sets the ResultSetMapping of the parsed query. + * + * @param ResultSetMapping $rsm + */ + public function setResultSetMapping(ResultSetMapping $rsm) + { + $this->_resultSetMapping = $rsm; + } + + /** + * Sets the SQL executor that should be used for this ParserResult. + * + * @param \Doctrine\ORM\Query\Exec\AbstractSqlExecutor $executor + */ + public function setSqlExecutor($executor) + { + $this->_sqlExecutor = $executor; + } + + /** + * Gets the SQL executor used by this ParserResult. + * + * @return \Doctrine\ORM\Query\Exec\AbstractSqlExecutor + */ + public function getSqlExecutor() + { + return $this->_sqlExecutor; + } + + /** + * Adds a DQL to SQL parameter mapping. One DQL parameter name/position can map to + * several SQL parameter positions. + * + * @param string|integer $dqlPosition + * @param integer $sqlPosition + */ + public function addParameterMapping($dqlPosition, $sqlPosition) + { + $this->_parameterMappings[$dqlPosition][] = $sqlPosition; + } + + /** + * Gets all DQL to SQL parameter mappings. + * + * @return array The parameter mappings. + */ + public function getParameterMappings() + { + return $this->_parameterMappings; + } + + /** + * Gets the SQL parameter positions for a DQL parameter name/position. + * + * @param string|integer $dqlPosition The name or position of the DQL parameter. + * @return array The positions of the corresponding SQL parameters. + */ + public function getSqlParameterPositions($dqlPosition) + { + return $this->_parameterMappings[$dqlPosition]; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Printer.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Printer.php new file mode 100644 index 0000000..972ad51 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Printer.php @@ -0,0 +1,95 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * A parse tree printer for Doctrine Query Language parser. + * + * @author Janne Vanhala + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://www.phpdoctrine.org + * @since 2.0 + * @version $Revision$ + */ +class Printer +{ + /** + * Current indentation level + * + * @var int + */ + protected $_indent = 0; + + /** + * Defines whether parse tree is printed (default, false) or not (true). + * + * @var bool + */ + protected $_silent; + + /** + * Constructs a new parse tree printer. + * + * @param bool $silent Parse tree will not be printed if true. + */ + public function __construct($silent = false) + { + $this->_silent = $silent; + } + + /** + * Prints an opening parenthesis followed by production name and increases + * indentation level by one. + * + * This method is called before executing a production. + * + * @param string $name production name + */ + public function startProduction($name) + { + $this->println('(' . $name); + $this->_indent++; + } + + /** + * Decreases indentation level by one and prints a closing parenthesis. + * + * This method is called after executing a production. + */ + public function endProduction() + { + $this->_indent--; + $this->println(')'); + } + + /** + * Prints text indented with spaces depending on current indentation level. + * + * @param string $str text + */ + public function println($str) + { + if ( ! $this->_silent) { + echo str_repeat(' ', $this->_indent), $str, "\n"; + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php new file mode 100644 index 0000000..cd74f35 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php @@ -0,0 +1,154 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\Query\AST\PathExpression; + +/** + * Description of QueryException + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision: 3938 $ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class QueryException extends \Doctrine\ORM\ORMException +{ + public static function syntaxError($message) + { + return new self('[Syntax Error] ' . $message); + } + + public static function semanticalError($message) + { + return new self('[Semantical Error] ' . $message); + } + + public static function invalidLockMode() + { + return new self('Invalid lock mode hint provided.'); + } + + public static function invalidParameterType($expected, $received) + { + return new self('Invalid parameter type, ' . $received . ' given, but ' . $expected . ' expected.'); + } + + public static function invalidParameterPosition($pos) + { + return new self('Invalid parameter position: ' . $pos); + } + + public static function invalidParameterNumber() + { + return new self("Invalid parameter number: number of bound variables does not match number of tokens"); + } + + public static function invalidParameterFormat($value) + { + return new self('Invalid parameter format, '.$value.' given, but : or ? expected.'); + } + + public static function unknownParameter($key) + { + return new self("Invalid parameter: token ".$key." is not defined in the query."); + } + + public static function parameterTypeMissmatch() + { + return new self("DQL Query parameter and type numbers missmatch, but have to be exactly equal."); + } + + public static function invalidPathExpression($pathExpr) + { + return new self( + "Invalid PathExpression '" . $pathExpr->identificationVariable . "." . $pathExpr->field . "'." + ); + } + + public static function invalidLiteral($literal) { + return new self("Invalid literal '$literal'"); + } + + /** + * @param \Doctrine\ORM\Mapping\AssociationMapping $assoc + */ + public static function iterateWithFetchJoinCollectionNotAllowed($assoc) + { + return new self( + "Invalid query operation: Not allowed to iterate over fetch join collections ". + "in class ".$assoc['sourceEntity']." assocation ".$assoc['fieldName'] + ); + } + + public static function partialObjectsAreDangerous() + { + return new self( + "Loading partial objects is dangerous. Fetch full objects or consider " . + "using a different fetch mode. If you really want partial objects, " . + "set the doctrine.forcePartialLoad query hint to TRUE." + ); + } + + public static function overwritingJoinConditionsNotYetSupported($assoc) + { + return new self( + "Unsupported query operation: It is not yet possible to overwrite the join ". + "conditions in class ".$assoc['sourceEntityName']." assocation ".$assoc['fieldName'].". ". + "Use WITH to append additional join conditions to the association." + ); + } + + public static function associationPathInverseSideNotSupported() + { + return new self( + "A single-valued association path expression to an inverse side is not supported". + " in DQL queries. Use an explicit join instead." + ); + } + + public static function iterateWithFetchJoinNotAllowed($assoc) { + return new self( + "Iterate with fetch join in class " . $assoc['sourceEntity'] . + " using association " . $assoc['fieldName'] . " not allowed." + ); + } + + public static function associationPathCompositeKeyNotSupported() + { + return new self( + "A single-valued association path expression to an entity with a composite primary ". + "key is not supported. Explicitly name the components of the composite primary key ". + "in the query." + ); + } + + public static function instanceOfUnrelatedClass($className, $rootClass) + { + return new self("Cannot check if a child of '" . $rootClass . "' is instanceof '" . $className . "', " . + "inheritance hierachy exists between these two classes."); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMapping.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMapping.php new file mode 100644 index 0000000..32aa298 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -0,0 +1,485 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * A ResultSetMapping describes how a result set of an SQL query maps to a Doctrine result. + * + * IMPORTANT NOTE: + * The properties of this class are only public for fast internal READ access and to (drastically) + * reduce the size of serialized instances for more effective caching due to better (un-)serialization + * performance. + * + * Users should use the public methods. + * + * @author Roman Borschel + * @since 2.0 + * @todo Think about whether the number of lookup maps can be reduced. + */ +class ResultSetMapping +{ + /** + * @ignore + * @var boolean Whether the result is mixed (contains scalar values together with field values). + */ + public $isMixed = false; + + /** + * @ignore + * @var array Maps alias names to class names. + */ + public $aliasMap = array(); + + /** + * @ignore + * @var array Maps alias names to related association field names. + */ + public $relationMap = array(); + + /** + * @ignore + * @var array Maps alias names to parent alias names. + */ + public $parentAliasMap = array(); + + /** + * @ignore + * @var array Maps column names in the result set to field names for each class. + */ + public $fieldMappings = array(); + + /** + * @ignore + * @var array Maps column names in the result set to the alias/field name to use in the mapped result. + */ + public $scalarMappings = array(); + + /** + * @ignore + * @var array Maps column names in the result set to the alias/field type to use in the mapped result. + */ + public $typeMappings = array(); + + /** + * @ignore + * @var array Maps entities in the result set to the alias name to use in the mapped result. + */ + public $entityMappings = array(); + + /** + * @ignore + * @var array Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names. + */ + public $metaMappings = array(); + + /** + * @ignore + * @var array Maps column names in the result set to the alias they belong to. + */ + public $columnOwnerMap = array(); + + /** + * @ignore + * @var array List of columns in the result set that are used as discriminator columns. + */ + public $discriminatorColumns = array(); + + /** + * @ignore + * @var array Maps alias names to field names that should be used for indexing. + */ + public $indexByMap = array(); + + /** + * @ignore + * @var array Map from column names to class names that declare the field the column is mapped to. + */ + public $declaringClasses = array(); + + /** + * @var array This is necessary to hydrate derivate foreign keys correctly. + */ + public $isIdentifierColumn = array(); + + /** + * Adds an entity result to this ResultSetMapping. + * + * @param string $class The class name of the entity. + * @param string $alias The alias for the class. The alias must be unique among all entity + * results or joined entity results within this ResultSetMapping. + * @param string $resultAlias The result alias with which the entity result should be + * placed in the result structure. + * @return ResultSetMapping This ResultSetMapping instance. + * @todo Rename: addRootEntity + */ + public function addEntityResult($class, $alias, $resultAlias = null) + { + $this->aliasMap[$alias] = $class; + $this->entityMappings[$alias] = $resultAlias; + + if ($resultAlias !== null) { + $this->isMixed = true; + } + + return $this; + } + + /** + * Sets a discriminator column for an entity result or joined entity result. + * The discriminator column will be used to determine the concrete class name to + * instantiate. + * + * @param string $alias The alias of the entity result or joined entity result the discriminator + * column should be used for. + * @param string $discrColumn The name of the discriminator column in the SQL result set. + * @return ResultSetMapping This ResultSetMapping instance. + * @todo Rename: addDiscriminatorColumn + */ + public function setDiscriminatorColumn($alias, $discrColumn) + { + $this->discriminatorColumns[$alias] = $discrColumn; + $this->columnOwnerMap[$discrColumn] = $alias; + + return $this; + } + + /** + * Sets a field to use for indexing an entity result or joined entity result. + * + * @param string $alias The alias of an entity result or joined entity result. + * @param string $fieldName The name of the field to use for indexing. + * @return ResultSetMapping This ResultSetMapping instance. + */ + public function addIndexBy($alias, $fieldName) + { + $found = false; + + foreach ($this->fieldMappings AS $columnName => $columnFieldName) { + if ( ! ($columnFieldName === $fieldName && $this->columnOwnerMap[$columnName] === $alias)) continue; + + $this->addIndexByColumn($alias, $columnName); + $found = true; + + break; + } + + /* TODO: check if this exception can be put back, for now it's gone because of assumptions made by some ORM internals + if ( ! $found) { + $message = sprintf( + 'Cannot add index by for DQL alias %s and field %s without calling addFieldResult() for them before.', + $alias, + $fieldName + ); + + throw new \LogicException($message); + } + */ + + return $this; + } + + /** + * Set to index by a scalar result column name + * + * @param $resultColumnName + * @return ResultSetMapping This ResultSetMapping instance. + */ + public function addIndexByScalar($resultColumnName) + { + $this->indexByMap['scalars'] = $resultColumnName; + + return $this; + } + + /** + * Sets a column to use for indexing an entity or joined entity result by the given alias name. + * + * @param $alias + * @param $resultColumnName + * @return ResultSetMapping This ResultSetMapping instance. + */ + public function addIndexByColumn($alias, $resultColumnName) + { + $this->indexByMap[$alias] = $resultColumnName; + + return $this; + } + + /** + * Checks whether an entity result or joined entity result with a given alias has + * a field set for indexing. + * + * @param string $alias + * @return boolean + * @todo Rename: isIndexed($alias) + */ + public function hasIndexBy($alias) + { + return isset($this->indexByMap[$alias]); + } + + /** + * Checks whether the column with the given name is mapped as a field result + * as part of an entity result or joined entity result. + * + * @param string $columnName The name of the column in the SQL result set. + * @return boolean + * @todo Rename: isField + */ + public function isFieldResult($columnName) + { + return isset($this->fieldMappings[$columnName]); + } + + /** + * Adds a field to the result that belongs to an entity or joined entity. + * + * @param string $alias The alias of the root entity or joined entity to which the field belongs. + * @param string $columnName The name of the column in the SQL result set. + * @param string $fieldName The name of the field on the declaring class. + * @param string $declaringClass The name of the class that declares/owns the specified field. + * When $alias refers to a superclass in a mapped hierarchy but + * the field $fieldName is defined on a subclass, specify that here. + * If not specified, the field is assumed to belong to the class + * designated by $alias. + * @return ResultSetMapping This ResultSetMapping instance. + * @todo Rename: addField + */ + public function addFieldResult($alias, $columnName, $fieldName, $declaringClass = null) + { + // column name (in result set) => field name + $this->fieldMappings[$columnName] = $fieldName; + // column name => alias of owner + $this->columnOwnerMap[$columnName] = $alias; + // field name => class name of declaring class + $this->declaringClasses[$columnName] = $declaringClass ?: $this->aliasMap[$alias]; + + if ( ! $this->isMixed && $this->scalarMappings) { + $this->isMixed = true; + } + + return $this; + } + + /** + * Adds a joined entity result. + * + * @param string $class The class name of the joined entity. + * @param string $alias The unique alias to use for the joined entity. + * @param string $parentAlias The alias of the entity result that is the parent of this joined result. + * @param object $relation The association field that connects the parent entity result with the joined entity result. + * @return ResultSetMapping This ResultSetMapping instance. + * @todo Rename: addJoinedEntity + */ + public function addJoinedEntityResult($class, $alias, $parentAlias, $relation) + { + $this->aliasMap[$alias] = $class; + $this->parentAliasMap[$alias] = $parentAlias; + $this->relationMap[$alias] = $relation; + + return $this; + } + + /** + * Adds a scalar result mapping. + * + * @param string $columnName The name of the column in the SQL result set. + * @param string $alias The result alias with which the scalar result should be placed in the result structure. + * @param string $type The column type + * + * @return ResultSetMapping This ResultSetMapping instance. + * + * @todo Rename: addScalar + */ + public function addScalarResult($columnName, $alias, $type = null) + { + $this->scalarMappings[$columnName] = $alias; + $this->typeMappings[$columnName] = $type ?: 'string'; + + if ( ! $this->isMixed && $this->fieldMappings) { + $this->isMixed = true; + } + + return $this; + } + + /** + * Checks whether a column with a given name is mapped as a scalar result. + * + * @param string $columName The name of the column in the SQL result set. + * @return boolean + * @todo Rename: isScalar + */ + public function isScalarResult($columnName) + { + return isset($this->scalarMappings[$columnName]); + } + + /** + * Gets the name of the class of an entity result or joined entity result, + * identified by the given unique alias. + * + * @param string $alias + * @return string + */ + public function getClassName($alias) + { + return $this->aliasMap[$alias]; + } + + /** + * Gets the field alias for a column that is mapped as a scalar value. + * + * @param string $columnName The name of the column in the SQL result set. + * @return string + */ + public function getScalarAlias($columnName) + { + return $this->scalarMappings[$columnName]; + } + + /** + * Gets the name of the class that owns a field mapping for the specified column. + * + * @param string $columnName + * @return string + */ + public function getDeclaringClass($columnName) + { + return $this->declaringClasses[$columnName]; + } + + /** + * + * @param string $alias + * @return AssociationMapping + */ + public function getRelation($alias) + { + return $this->relationMap[$alias]; + } + + /** + * + * @param string $alias + * @return boolean + */ + public function isRelation($alias) + { + return isset($this->relationMap[$alias]); + } + + /** + * Gets the alias of the class that owns a field mapping for the specified column. + * + * @param string $columnName + * @return string + */ + public function getEntityAlias($columnName) + { + return $this->columnOwnerMap[$columnName]; + } + + /** + * Gets the parent alias of the given alias. + * + * @param string $alias + * @return string + */ + public function getParentAlias($alias) + { + return $this->parentAliasMap[$alias]; + } + + /** + * Checks whether the given alias has a parent alias. + * + * @param string $alias + * @return boolean + */ + public function hasParentAlias($alias) + { + return isset($this->parentAliasMap[$alias]); + } + + /** + * Gets the field name for a column name. + * + * @param string $columnName + * @return string + */ + public function getFieldName($columnName) + { + return $this->fieldMappings[$columnName]; + } + + /** + * + * @return array + */ + public function getAliasMap() + { + return $this->aliasMap; + } + + /** + * Gets the number of different entities that appear in the mapped result. + * + * @return integer + */ + public function getEntityResultCount() + { + return count($this->aliasMap); + } + + /** + * Checks whether this ResultSetMapping defines a mixed result. + * Mixed results can only occur in object and array (graph) hydration. In such a + * case a mixed result means that scalar values are mixed with objects/array in + * the result. + * + * @return boolean + */ + public function isMixedResult() + { + return $this->isMixed; + } + + /** + * Adds a meta column (foreign key or discriminator column) to the result set. + * + * @param string $alias + * @param string $columnName + * @param string $fieldName + * @param bool + * @return ResultSetMapping This ResultSetMapping instance. + */ + public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false) + { + $this->metaMappings[$columnName] = $fieldName; + $this->columnOwnerMap[$columnName] = $alias; + + if ($isIdentifierColumn) { + $this->isIdentifierColumn[$alias][$columnName] = true; + } + + return $this; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php new file mode 100644 index 0000000..f84686a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php @@ -0,0 +1,109 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * A ResultSetMappingBuilder uses the EntityManager to automatically populate entity fields + * + * @author Michael Ridgway + * @since 2.1 + */ +class ResultSetMappingBuilder extends ResultSetMapping +{ + /** + * @var EntityManager + */ + private $em; + + /** + * @param EntityManager + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + } + + /** + * Adds a root entity and all of its fields to the result set. + * + * @param string $class The class name of the root entity. + * @param string $alias The unique alias to use for the root entity. + * @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName) + */ + public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = array()) + { + $this->addEntityResult($class, $alias); + $this->addAllClassFields($class, $alias, $renamedColumns); + } + + /** + * Adds a joined entity and all of its fields to the result set. + * + * @param string $class The class name of the joined entity. + * @param string $alias The unique alias to use for the joined entity. + * @param string $parentAlias The alias of the entity result that is the parent of this joined result. + * @param object $relation The association field that connects the parent entity result with the joined entity result. + * @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName) + */ + public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $renamedColumns = array()) + { + $this->addJoinedEntityResult($class, $alias, $parentAlias, $relation); + $this->addAllClassFields($class, $alias, $renamedColumns); + } + + /** + * Adds all fields of the given class to the result set mapping (columns and meta fields) + */ + protected function addAllClassFields($class, $alias, $renamedColumns = array()) + { + $classMetadata = $this->em->getClassMetadata($class); + if ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()) { + throw new \InvalidArgumentException('ResultSetMapping builder does not currently support inheritance.'); + } + $platform = $this->em->getConnection()->getDatabasePlatform(); + foreach ($classMetadata->getColumnNames() AS $columnName) { + $propertyName = $classMetadata->getFieldName($columnName); + if (isset($renamedColumns[$columnName])) { + $columnName = $renamedColumns[$columnName]; + } + $columnName = $platform->getSQLResultCasing($columnName); + if (isset($this->fieldMappings[$columnName])) { + throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper."); + } + $this->addFieldResult($alias, $columnName, $propertyName); + } + foreach ($classMetadata->associationMappings AS $associationMapping) { + if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + foreach ($associationMapping['joinColumns'] AS $joinColumn) { + $columnName = $joinColumn['name']; + $renamedColumnName = isset($renamedColumns[$columnName]) ? $renamedColumns[$columnName] : $columnName; + $renamedColumnName = $platform->getSQLResultCasing($renamedColumnName); + if (isset($this->metaMappings[$renamedColumnName])) { + throw new \InvalidArgumentException("The column '$renamedColumnName' conflicts with another column in the mapper."); + } + $this->addMetaResult($alias, $renamedColumnName, $columnName); + } + } + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/SqlWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/SqlWalker.php new file mode 100644 index 0000000..d883aea --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/SqlWalker.php @@ -0,0 +1,2159 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\DBAL\LockMode, + Doctrine\DBAL\Types\Type, + Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\Query, + Doctrine\ORM\Query\QueryException, + Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * The SqlWalker is a TreeWalker that walks over a DQL AST and constructs + * the corresponding SQL. + * + * @author Guilherme Blanco + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Alexander + * @since 2.0 + * @todo Rename: SQLWalker + */ +class SqlWalker implements TreeWalker +{ + /** + * @var string + */ + const HINT_DISTINCT = 'doctrine.distinct'; + + /** + * @var ResultSetMapping + */ + private $_rsm; + + /** Counters for generating unique column aliases, table aliases and parameter indexes. */ + private $_aliasCounter = 0; + private $_tableAliasCounter = 0; + private $_scalarResultCounter = 1; + private $_sqlParamIndex = 0; + + /** + * @var ParserResult + */ + private $_parserResult; + + /** + * @var EntityManager + */ + private $_em; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $_conn; + + /** + * @var AbstractQuery + */ + private $_query; + + private $_tableAliasMap = array(); + + /** Map from result variable names to their SQL column alias names. */ + private $_scalarResultAliasMap = array(); + + /** + * Map from DQL-Alias + Field-Name to SQL Column Alias + * + * @var array + */ + private $_scalarFields = array(); + + /** Map of all components/classes that appear in the DQL query. */ + private $_queryComponents; + + /** A list of classes that appear in non-scalar SelectExpressions. */ + private $_selectedClasses = array(); + + /** + * The DQL alias of the root class of the currently traversed query. + */ + private $_rootAliases = array(); + + /** + * Flag that indicates whether to generate SQL table aliases in the SQL. + * These should only be generated for SELECT queries, not for UPDATE/DELETE. + */ + private $_useSqlTableAliases = true; + + /** + * The database platform abstraction. + * + * @var AbstractPlatform + */ + private $_platform; + + /** + * {@inheritDoc} + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->_query = $query; + $this->_parserResult = $parserResult; + $this->_queryComponents = $queryComponents; + $this->_rsm = $parserResult->getResultSetMapping(); + $this->_em = $query->getEntityManager(); + $this->_conn = $this->_em->getConnection(); + $this->_platform = $this->_conn->getDatabasePlatform(); + } + + /** + * Gets the Query instance used by the walker. + * + * @return Query. + */ + public function getQuery() + { + return $this->_query; + } + + /** + * Gets the Connection used by the walker. + * + * @return Connection + */ + public function getConnection() + { + return $this->_conn; + } + + /** + * Gets the EntityManager used by the walker. + * + * @return EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * Gets the information about a single query component. + * + * @param string $dqlAlias The DQL alias. + * @return array + */ + public function getQueryComponent($dqlAlias) + { + return $this->_queryComponents[$dqlAlias]; + } + + /** + * Gets an executor that can be used to execute the result of this walker. + * + * @return AbstractExecutor + */ + public function getExecutor($AST) + { + switch (true) { + case ($AST instanceof AST\DeleteStatement): + $primaryClass = $this->_em->getClassMetadata($AST->deleteClause->abstractSchemaName); + + return ($primaryClass->isInheritanceTypeJoined()) + ? new Exec\MultiTableDeleteExecutor($AST, $this) + : new Exec\SingleTableDeleteUpdateExecutor($AST, $this); + + case ($AST instanceof AST\UpdateStatement): + $primaryClass = $this->_em->getClassMetadata($AST->updateClause->abstractSchemaName); + + return ($primaryClass->isInheritanceTypeJoined()) + ? new Exec\MultiTableUpdateExecutor($AST, $this) + : new Exec\SingleTableDeleteUpdateExecutor($AST, $this); + + default: + return new Exec\SingleSelectExecutor($AST, $this); + } + } + + /** + * Generates a unique, short SQL table alias. + * + * @param string $tableName Table name + * @param string $dqlAlias The DQL alias. + * @return string Generated table alias. + */ + public function getSQLTableAlias($tableName, $dqlAlias = '') + { + $tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : ''; + + if ( ! isset($this->_tableAliasMap[$tableName])) { + $this->_tableAliasMap[$tableName] = strtolower(substr($tableName, 0, 1)) . $this->_tableAliasCounter++ . '_'; + } + + return $this->_tableAliasMap[$tableName]; + } + + /** + * Forces the SqlWalker to use a specific alias for a table name, rather than + * generating an alias on its own. + * + * @param string $tableName + * @param string $alias + * @param string $dqlAlias + * @return string + */ + public function setSQLTableAlias($tableName, $alias, $dqlAlias = '') + { + $tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : ''; + + $this->_tableAliasMap[$tableName] = $alias; + + return $alias; + } + + /** + * Gets an SQL column alias for a column name. + * + * @param string $columnName + * @return string + */ + public function getSQLColumnAlias($columnName) + { + // Trim the column alias to the maximum identifier length of the platform. + // If the alias is to long, characters are cut off from the beginning. + return $this->_platform->getSQLResultCasing( + substr($columnName . $this->_aliasCounter++, -$this->_platform->getMaxIdentifierLength()) + ); + } + + /** + * Generates the SQL JOINs that are necessary for Class Table Inheritance + * for the given class. + * + * @param ClassMetadata $class The class for which to generate the joins. + * @param string $dqlAlias The DQL alias of the class. + * @return string The SQL. + */ + private function _generateClassTableInheritanceJoins($class, $dqlAlias) + { + $sql = ''; + + $baseTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + + // INNER JOIN parent class tables + foreach ($class->parentClasses as $parentClassName) { + $parentClass = $this->_em->getClassMetadata($parentClassName); + $tableAlias = $this->getSQLTableAlias($parentClass->getTableName(), $dqlAlias); + + // If this is a joined association we must use left joins to preserve the correct result. + $sql .= isset($this->_queryComponents[$dqlAlias]['relation']) ? ' LEFT ' : ' INNER '; + $sql .= 'JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; + + $sqlParts = array(); + + foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) { + $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName; + } + + // Add filters on the root class + if ($filterSql = $this->generateFilterConditionSQL($parentClass, $tableAlias)) { + $sqlParts[] = $filterSql; + } + + $sql .= implode(' AND ', $sqlParts); + } + + // Ignore subclassing inclusion if partial objects is disallowed + if ($this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { + return $sql; + } + + // LEFT JOIN child class tables + foreach ($class->subClasses as $subClassName) { + $subClass = $this->_em->getClassMetadata($subClassName); + $tableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); + + $sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; + + $sqlParts = array(); + + foreach ($subClass->getQuotedIdentifierColumnNames($this->_platform) as $columnName) { + $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName; + } + + $sql .= implode(' AND ', $sqlParts); + } + + return $sql; + } + + private function _generateOrderedCollectionOrderByItems() + { + $sqlParts = array(); + + foreach ($this->_selectedClasses AS $selectedClass) { + $dqlAlias = $selectedClass['dqlAlias']; + $qComp = $this->_queryComponents[$dqlAlias]; + + if ( ! isset($qComp['relation']['orderBy'])) continue; + + foreach ($qComp['relation']['orderBy'] AS $fieldName => $orientation) { + $columnName = $qComp['metadata']->getQuotedColumnName($fieldName, $this->_platform); + $tableName = ($qComp['metadata']->isInheritanceTypeJoined()) + ? $this->_em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name)->getOwningTable($fieldName) + : $qComp['metadata']->getTableName(); + + $sqlParts[] = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName . ' ' . $orientation; + } + } + + return implode(', ', $sqlParts); + } + + /** + * Generates a discriminator column SQL condition for the class with the given DQL alias. + * + * @param array $dqlAliases List of root DQL aliases to inspect for discriminator restrictions. + * @return string + */ + private function _generateDiscriminatorColumnConditionSQL(array $dqlAliases) + { + $sqlParts = array(); + + foreach ($dqlAliases as $dqlAlias) { + $class = $this->_queryComponents[$dqlAlias]['metadata']; + + if ( ! $class->isInheritanceTypeSingleTable()) continue; + + $conn = $this->_em->getConnection(); + $values = array(); + + if ($class->discriminatorValue !== null) { // discrimnators can be 0 + $values[] = $conn->quote($class->discriminatorValue); + } + + foreach ($class->subClasses as $subclassName) { + $values[] = $conn->quote($this->_em->getClassMetadata($subclassName)->discriminatorValue); + } + + $sqlParts[] = (($this->_useSqlTableAliases) ? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.' : '') + . $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')'; + } + + $sql = implode(' AND ', $sqlParts); + + return (count($sqlParts) > 1) ? '(' . $sql . ')' : $sql; + } + + /** + * Generates the filter SQL for a given entity and table alias. + * + * @param ClassMetadata $targetEntity Metadata of the target entity. + * @param string $targetTableAlias The table alias of the joined/selected table. + * + * @return string The SQL query part to add to a query. + */ + private function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + { + if (!$this->_em->hasFilters()) { + return ''; + } + + switch($targetEntity->inheritanceType) { + case ClassMetadata::INHERITANCE_TYPE_NONE: + break; + case ClassMetadata::INHERITANCE_TYPE_JOINED: + // The classes in the inheritance will be added to the query one by one, + // but only the root node is getting filtered + if ($targetEntity->name !== $targetEntity->rootEntityName) { + return ''; + } + break; + case ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE: + // With STI the table will only be queried once, make sure that the filters + // are added to the root entity + $targetEntity = $this->_em->getClassMetadata($targetEntity->rootEntityName); + break; + default: + //@todo: throw exception? + return ''; + break; + } + + $filterClauses = array(); + foreach ($this->_em->getFilters()->getEnabledFilters() as $filter) { + if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) { + $filterClauses[] = '(' . $filterExpr . ')'; + } + } + + return implode(' AND ', $filterClauses); + } + /** + * Walks down a SelectStatement AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkSelectStatement(AST\SelectStatement $AST) + { + $sql = $this->walkSelectClause($AST->selectClause); + $sql .= $this->walkFromClause($AST->fromClause); + $sql .= $this->walkWhereClause($AST->whereClause); + $sql .= $AST->groupByClause ? $this->walkGroupByClause($AST->groupByClause) : ''; + $sql .= $AST->havingClause ? $this->walkHavingClause($AST->havingClause) : ''; + + if (($orderByClause = $AST->orderByClause) !== null) { + $sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : ''; + } else if (($orderBySql = $this->_generateOrderedCollectionOrderByItems()) !== '') { + $sql .= ' ORDER BY ' . $orderBySql; + } + + $sql = $this->_platform->modifyLimitQuery( + $sql, $this->_query->getMaxResults(), $this->_query->getFirstResult() + ); + + if (($lockMode = $this->_query->getHint(Query::HINT_LOCK_MODE)) !== false) { + switch ($lockMode) { + case LockMode::PESSIMISTIC_READ: + $sql .= ' ' . $this->_platform->getReadLockSQL(); + break; + + case LockMode::PESSIMISTIC_WRITE: + $sql .= ' ' . $this->_platform->getWriteLockSQL(); + break; + + case LockMode::OPTIMISTIC: + foreach ($this->_selectedClasses AS $selectedClass) { + if ( ! $selectedClass['class']->isVersioned) { + throw \Doctrine\ORM\OptimisticLockException::lockFailed($selectedClass['class']->name); + } + } + break; + case LockMode::NONE: + break; + + default: + throw \Doctrine\ORM\Query\QueryException::invalidLockMode(); + } + } + + return $sql; + } + + /** + * Walks down an UpdateStatement AST node, thereby generating the appropriate SQL. + * + * @param UpdateStatement + * @return string The SQL. + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) + { + $this->_useSqlTableAliases = false; + + return $this->walkUpdateClause($AST->updateClause) + . $this->walkWhereClause($AST->whereClause); + } + + /** + * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL. + * + * @param DeleteStatement + * @return string The SQL. + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) + { + $this->_useSqlTableAliases = false; + + return $this->walkDeleteClause($AST->deleteClause) + . $this->walkWhereClause($AST->whereClause); + } + + /** + * Walks down an IdentificationVariable AST node, thereby generating the appropriate SQL. + * This one differs of ->walkIdentificationVariable() because it generates the entity identifiers. + * + * @param string $identVariable + * @return string + */ + public function walkEntityIdentificationVariable($identVariable) + { + $class = $this->_queryComponents[$identVariable]['metadata']; + $tableAlias = $this->getSQLTableAlias($class->getTableName(), $identVariable); + $sqlParts = array(); + + foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) { + $sqlParts[] = $tableAlias . '.' . $columnName; + } + + return implode(', ', $sqlParts); + } + + /** + * Walks down an IdentificationVariable (no AST node associated), thereby generating the SQL. + * + * @param string $identificationVariable + * @param string $fieldName + * @return string The SQL. + */ + public function walkIdentificationVariable($identificationVariable, $fieldName = null) + { + $class = $this->_queryComponents[$identificationVariable]['metadata']; + + if ( + $fieldName !== null && $class->isInheritanceTypeJoined() && + isset($class->fieldMappings[$fieldName]['inherited']) + ) { + $class = $this->_em->getClassMetadata($class->fieldMappings[$fieldName]['inherited']); + } + + return $this->getSQLTableAlias($class->getTableName(), $identificationVariable); + } + + /** + * Walks down a PathExpression AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkPathExpression($pathExpr) + { + $sql = ''; + + switch ($pathExpr->type) { + case AST\PathExpression::TYPE_STATE_FIELD: + $fieldName = $pathExpr->field; + $dqlAlias = $pathExpr->identificationVariable; + $class = $this->_queryComponents[$dqlAlias]['metadata']; + + if ($this->_useSqlTableAliases) { + $sql .= $this->walkIdentificationVariable($dqlAlias, $fieldName) . '.'; + } + + $sql .= $class->getQuotedColumnName($fieldName, $this->_platform); + break; + + case AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION: + // 1- the owning side: + // Just use the foreign key, i.e. u.group_id + $fieldName = $pathExpr->field; + $dqlAlias = $pathExpr->identificationVariable; + $class = $this->_queryComponents[$dqlAlias]['metadata']; + + if (isset($class->associationMappings[$fieldName]['inherited'])) { + $class = $this->_em->getClassMetadata($class->associationMappings[$fieldName]['inherited']); + } + + $assoc = $class->associationMappings[$fieldName]; + + if ( ! $assoc['isOwningSide']) { + throw QueryException::associationPathInverseSideNotSupported(); + } + + // COMPOSITE KEYS NOT (YET?) SUPPORTED + if (count($assoc['sourceToTargetKeyColumns']) > 1) { + throw QueryException::associationPathCompositeKeyNotSupported(); + } + + if ($this->_useSqlTableAliases) { + $sql .= $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.'; + } + + $sql .= reset($assoc['targetToSourceKeyColumns']); + break; + + default: + throw QueryException::invalidPathExpression($pathExpr); + } + + return $sql; + } + + /** + * Walks down a SelectClause AST node, thereby generating the appropriate SQL. + * + * @param $selectClause + * @return string The SQL. + */ + public function walkSelectClause($selectClause) + { + $sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : ''); + $sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions)); + + if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $selectClause->isDistinct) { + $this->_query->setHint(self::HINT_DISTINCT, true); + } + + $addMetaColumns = ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) && + $this->_query->getHydrationMode() == Query::HYDRATE_OBJECT + || + $this->_query->getHydrationMode() != Query::HYDRATE_OBJECT && + $this->_query->getHint(Query::HINT_INCLUDE_META_COLUMNS); + + foreach ($this->_selectedClasses as $selectedClass) { + $class = $selectedClass['class']; + $dqlAlias = $selectedClass['dqlAlias']; + $resultAlias = $selectedClass['resultAlias']; + + // Register as entity or joined entity result + if ($this->_queryComponents[$dqlAlias]['relation'] === null) { + $this->_rsm->addEntityResult($class->name, $dqlAlias, $resultAlias); + } else { + $this->_rsm->addJoinedEntityResult( + $class->name, + $dqlAlias, + $this->_queryComponents[$dqlAlias]['parent'], + $this->_queryComponents[$dqlAlias]['relation']['fieldName'] + ); + } + + if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) { + // Add discriminator columns to SQL + $rootClass = $this->_em->getClassMetadata($class->rootEntityName); + $tblAlias = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias); + $discrColumn = $rootClass->discriminatorColumn; + $columnAlias = $this->getSQLColumnAlias($discrColumn['name']); + + $sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias; + + $this->_rsm->setDiscriminatorColumn($dqlAlias, $columnAlias); + $this->_rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName']); + } + + // Add foreign key columns to SQL, if necessary + if ( ! $addMetaColumns && ! $class->containsForeignIdentifier) { + continue; + } + + // Add foreign key columns of class and also parent classes + foreach ($class->associationMappings as $assoc) { + if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) { + continue; + } else if ( !$addMetaColumns && !isset($assoc['id'])) { + continue; + } + + $owningClass = (isset($assoc['inherited'])) ? $this->_em->getClassMetadata($assoc['inherited']) : $class; + $sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias); + + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { + $columnAlias = $this->getSQLColumnAlias($srcColumn); + + $sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; + + $this->_rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn, (isset($assoc['id']) && $assoc['id'] === true)); + } + } + + // Add foreign key columns to SQL, if necessary + if ( ! $addMetaColumns) { + continue; + } + + // Add foreign key columns of subclasses + foreach ($class->subClasses as $subClassName) { + $subClass = $this->_em->getClassMetadata($subClassName); + $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); + + foreach ($subClass->associationMappings as $assoc) { + // Skip if association is inherited + if (isset($assoc['inherited'])) continue; + + if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) continue; + + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { + $columnAlias = $this->getSQLColumnAlias($srcColumn); + + $sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; + + $this->_rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn); + } + } + } + } + + $sql .= implode(', ', $sqlSelectExpressions); + + return $sql; + } + + /** + * Walks down a FromClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkFromClause($fromClause) + { + $identificationVarDecls = $fromClause->identificationVariableDeclarations; + $sqlParts = array(); + + foreach ($identificationVarDecls as $identificationVariableDecl) { + $sql = ''; + + $rangeDecl = $identificationVariableDecl->rangeVariableDeclaration; + $dqlAlias = $rangeDecl->aliasIdentificationVariable; + + $this->_rootAliases[] = $dqlAlias; + + $class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName); + $sql .= $class->getQuotedTableName($this->_platform) . ' ' + . $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + + if ($class->isInheritanceTypeJoined()) { + $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias); + } + + foreach ($identificationVariableDecl->joinVariableDeclarations as $joinVarDecl) { + $sql .= $this->walkJoinVariableDeclaration($joinVarDecl); + } + + if ($identificationVariableDecl->indexBy) { + $alias = $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable; + $field = $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field; + + if (isset($this->_scalarFields[$alias][$field])) { + $this->_rsm->addIndexByScalar($this->_scalarFields[$alias][$field]); + } else { + $this->_rsm->addIndexBy( + $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable, + $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field + ); + } + } + + $sqlParts[] = $this->_platform->appendLockHint($sql, $this->_query->getHint(Query::HINT_LOCK_MODE)); + } + + return ' FROM ' . implode(', ', $sqlParts); + } + + /** + * Walks down a FunctionNode AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkFunction($function) + { + return $function->getSql($this); + } + + /** + * Walks down an OrderByClause AST node, thereby generating the appropriate SQL. + * + * @param OrderByClause + * @return string The SQL. + */ + public function walkOrderByClause($orderByClause) + { + $orderByItems = array_map(array($this, 'walkOrderByItem'), $orderByClause->orderByItems); + + if (($collectionOrderByItems = $this->_generateOrderedCollectionOrderByItems()) !== '') { + $orderByItems = array_merge($orderByItems, (array) $collectionOrderByItems); + } + + return ' ORDER BY ' . implode(', ', $orderByItems); + } + + /** + * Walks down an OrderByItem AST node, thereby generating the appropriate SQL. + * + * @param OrderByItem + * @return string The SQL. + */ + public function walkOrderByItem($orderByItem) + { + $expr = $orderByItem->expression; + $sql = ($expr instanceof AST\PathExpression) + ? $this->walkPathExpression($expr) + : $this->walkResultVariable($this->_queryComponents[$expr]['token']['value']); + + return $sql . ' ' . strtoupper($orderByItem->type); + } + + /** + * Walks down a HavingClause AST node, thereby generating the appropriate SQL. + * + * @param HavingClause + * @return string The SQL. + */ + public function walkHavingClause($havingClause) + { + return ' HAVING ' . $this->walkConditionalExpression($havingClause->conditionalExpression); + } + + /** + * Walks down a JoinVariableDeclaration AST node and creates the corresponding SQL. + * + * @param JoinVariableDeclaration $joinVarDecl + * @return string The SQL. + */ + public function walkJoinVariableDeclaration($joinVarDecl) + { + $join = $joinVarDecl->join; + $joinType = $join->joinType; + $sql = ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) + ? ' LEFT JOIN ' + : ' INNER JOIN '; + + if ($joinVarDecl->indexBy) { + // For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently. + $this->_rsm->addIndexBy( + $joinVarDecl->indexBy->simpleStateFieldPathExpression->identificationVariable, + $joinVarDecl->indexBy->simpleStateFieldPathExpression->field + ); + } + + $joinAssocPathExpr = $join->joinAssociationPathExpression; + $joinedDqlAlias = $join->aliasIdentificationVariable; + + $relation = $this->_queryComponents[$joinedDqlAlias]['relation']; + $targetClass = $this->_em->getClassMetadata($relation['targetEntity']); + $sourceClass = $this->_em->getClassMetadata($relation['sourceEntity']); + $targetTableName = $targetClass->getQuotedTableName($this->_platform); + + $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias); + $sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $joinAssocPathExpr->identificationVariable); + + // Ensure we got the owning side, since it has all mapping info + $assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation; + if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true && (!$this->_query->getHint(self::HINT_DISTINCT) || isset($this->_selectedClasses[$joinedDqlAlias]))) { + if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) { + throw QueryException::iterateWithFetchJoinNotAllowed($assoc); + } + } + + if ($joinVarDecl->indexBy) { + // For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently. + $this->_rsm->addIndexBy( + $joinVarDecl->indexBy->simpleStateFieldPathExpression->identificationVariable, + $joinVarDecl->indexBy->simpleStateFieldPathExpression->field + ); + } else if (isset($relation['indexBy'])) { + $this->_rsm->addIndexBy($joinedDqlAlias, $relation['indexBy']); + } + + // This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot + // be the owning side and previously we ensured that $assoc is always the owning side of the associations. + // The owning side is necessary at this point because only it contains the JoinColumn information. + if ($assoc['type'] & ClassMetadata::TO_ONE) { + $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON '; + $first = true; + + foreach ($assoc['sourceToTargetKeyColumns'] as $sourceColumn => $targetColumn) { + if ( ! $first) $sql .= ' AND '; else $first = false; + + if ($relation['isOwningSide']) { + if ($targetClass->containsForeignIdentifier && !isset($targetClass->fieldNames[$targetColumn])) { + $quotedTargetColumn = $targetColumn; // Join columns cannot be quoted. + } else { + $quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform); + } + $sql .= $sourceTableAlias . '.' . $sourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn; + } else { + if ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$targetColumn])) { + $quotedTargetColumn = $targetColumn; // Join columns cannot be quoted. + } else { + $quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform); + } + $sql .= $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn; + } + } + + } else if ($assoc['type'] == ClassMetadata::MANY_TO_MANY) { + // Join relation table + $joinTable = $assoc['joinTable']; + $joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias); + $sql .= $sourceClass->getQuotedJoinTableName($assoc, $this->_platform) . ' ' . $joinTableAlias . ' ON '; + + $first = true; + if ($relation['isOwningSide']) { + foreach ($assoc['relationToSourceKeyColumns'] as $relationColumn => $sourceColumn) { + if ( ! $first) $sql .= ' AND '; else $first = false; + + if ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$sourceColumn])) { + $quotedTargetColumn = $sourceColumn; // Join columns cannot be quoted. + } else { + $quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform); + } + + $sql .= $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn; + } + } else { + foreach ($assoc['relationToTargetKeyColumns'] as $relationColumn => $targetColumn) { + if ( ! $first) $sql .= ' AND '; else $first = false; + + if ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$targetColumn])) { + $quotedTargetColumn = $targetColumn; // Join columns cannot be quoted. + } else { + $quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform); + } + + $sql .= $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn; + } + } + + // Join target table + $sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' : ' INNER JOIN '; + $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON '; + + $first = true; + if ($relation['isOwningSide']) { + foreach ($assoc['relationToTargetKeyColumns'] as $relationColumn => $targetColumn) { + if ( ! $first) $sql .= ' AND '; else $first = false; + + if ($targetClass->containsForeignIdentifier && !isset($targetClass->fieldNames[$targetColumn])) { + $quotedTargetColumn = $targetColumn; // Join columns cannot be quoted. + } else { + $quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform); + } + + $sql .= $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn; + } + } else { + foreach ($assoc['relationToSourceKeyColumns'] as $relationColumn => $sourceColumn) { + if ( ! $first) $sql .= ' AND '; else $first = false; + + if ($targetClass->containsForeignIdentifier && !isset($targetClass->fieldNames[$sourceColumn])) { + $quotedTargetColumn = $sourceColumn; // Join columns cannot be quoted. + } else { + $quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$sourceColumn], $this->_platform); + } + + $sql .= $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn; + } + } + } + + // Apply the filters + if ($filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias)) { + $sql .= ' AND ' . $filterExpr; + } + + // Handle WITH clause + if (($condExpr = $join->conditionalExpression) !== null) { + // Phase 2 AST optimization: Skip processment of ConditionalExpression + // if only one ConditionalTerm is defined + $sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')'; + } + + $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias)); + + if ($discrSql) { + $sql .= ' AND ' . $discrSql; + } + + // FIXME: these should either be nested or all forced to be left joins (DDC-XXX) + if ($targetClass->isInheritanceTypeJoined()) { + $sql .= $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias); + } + + return $sql; + } + + /** + * Walks down a CaseExpression AST node and generates the corresponding SQL. + * + * @param CoalesceExpression|NullIfExpression|GeneralCaseExpression|SimpleCaseExpression $expression + * @return string The SQL. + */ + public function walkCaseExpression($expression) + { + switch (true) { + case ($expression instanceof AST\CoalesceExpression): + return $this->walkCoalesceExpression($expression); + + case ($expression instanceof AST\NullIfExpression): + return $this->walkNullIfExpression($expression); + + case ($expression instanceof AST\GeneralCaseExpression): + return $this->walkGeneralCaseExpression($expression); + + case ($expression instanceof AST\SimpleCaseExpression): + return $this->walkSimpleCaseExpression($expression); + + default: + return ''; + } + } + + /** + * Walks down a CoalesceExpression AST node and generates the corresponding SQL. + * + * @param CoalesceExpression $coalesceExpression + * @return string The SQL. + */ + public function walkCoalesceExpression($coalesceExpression) + { + $sql = 'COALESCE('; + + $scalarExpressions = array(); + + foreach ($coalesceExpression->scalarExpressions as $scalarExpression) { + $scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression); + } + + $sql .= implode(', ', $scalarExpressions) . ')'; + + return $sql; + } + + /** + * Walks down a NullIfExpression AST node and generates the corresponding SQL. + * + * @param NullIfExpression $nullIfExpression + * @return string The SQL. + */ + public function walkNullIfExpression($nullIfExpression) + { + $firstExpression = is_string($nullIfExpression->firstExpression) + ? $this->_conn->quote($nullIfExpression->firstExpression) + : $this->walkSimpleArithmeticExpression($nullIfExpression->firstExpression); + + $secondExpression = is_string($nullIfExpression->secondExpression) + ? $this->_conn->quote($nullIfExpression->secondExpression) + : $this->walkSimpleArithmeticExpression($nullIfExpression->secondExpression); + + return 'NULLIF(' . $firstExpression . ', ' . $secondExpression . ')'; + } + + /** + * Walks down a GeneralCaseExpression AST node and generates the corresponding SQL. + * + * @param GeneralCaseExpression $generalCaseExpression + * @return string The SQL. + */ + public function walkGeneralCaseExpression(AST\GeneralCaseExpression $generalCaseExpression) + { + $sql = 'CASE'; + + foreach ($generalCaseExpression->whenClauses as $whenClause) { + $sql .= ' WHEN ' . $this->walkConditionalExpression($whenClause->caseConditionExpression); + $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($whenClause->thenScalarExpression); + } + + $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($generalCaseExpression->elseScalarExpression) . ' END'; + + return $sql; + } + + /** + * Walks down a SimpleCaseExpression AST node and generates the corresponding SQL. + * + * @param SimpleCaseExpression $simpleCaseExpression + * @return string The SQL. + */ + public function walkSimpleCaseExpression($simpleCaseExpression) + { + $sql = 'CASE ' . $this->walkStateFieldPathExpression($simpleCaseExpression->caseOperand); + + foreach ($simpleCaseExpression->simpleWhenClauses as $simpleWhenClause) { + $sql .= ' WHEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->caseScalarExpression); + $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->thenScalarExpression); + } + + $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($simpleCaseExpression->elseScalarExpression) . ' END'; + + return $sql; + } + + /** + * Walks down a SelectExpression AST node and generates the corresponding SQL. + * + * @param SelectExpression $selectExpression + * @return string The SQL. + */ + public function walkSelectExpression($selectExpression) + { + $sql = ''; + $expr = $selectExpression->expression; + $hidden = $selectExpression->hiddenAliasResultVariable; + + switch (true) { + case ($expr instanceof AST\PathExpression): + if ($expr->type !== AST\PathExpression::TYPE_STATE_FIELD) { + throw QueryException::invalidPathExpression($expr->type); + } + + $fieldName = $expr->field; + $dqlAlias = $expr->identificationVariable; + $qComp = $this->_queryComponents[$dqlAlias]; + $class = $qComp['metadata']; + + $resultAlias = $selectExpression->fieldIdentificationVariable ?: $fieldName; + $tableName = ($class->isInheritanceTypeJoined()) + ? $this->_em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName) + : $class->getTableName(); + + $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias); + $columnName = $class->getQuotedColumnName($fieldName, $this->_platform); + $columnAlias = $this->getSQLColumnAlias($class->fieldMappings[$fieldName]['columnName']); + + $col = $sqlTableAlias . '.' . $columnName; + + $fieldType = $class->getTypeOfField($fieldName); + + if (isset($class->fieldMappings[$fieldName]['requireSQLConversion'])) { + $type = Type::getType($fieldType); + $col = $type->convertToPHPValueSQL($col, $this->_conn->getDatabasePlatform()); + } + + $sql .= $col . ' AS ' . $columnAlias; + + $this->_scalarResultAliasMap[$resultAlias] = $columnAlias; + + if ( ! $hidden) { + $this->_rsm->addScalarResult($columnAlias, $resultAlias, $fieldType); + $this->_scalarFields[$dqlAlias][$fieldName] = $columnAlias; + } + break; + + case ($expr instanceof AST\AggregateExpression): + case ($expr instanceof AST\Functions\FunctionNode): + case ($expr instanceof AST\SimpleArithmeticExpression): + case ($expr instanceof AST\ArithmeticTerm): + case ($expr instanceof AST\ArithmeticFactor): + case ($expr instanceof AST\ArithmeticPrimary): + case ($expr instanceof AST\Literal): + case ($expr instanceof AST\NullIfExpression): + case ($expr instanceof AST\CoalesceExpression): + case ($expr instanceof AST\GeneralCaseExpression): + case ($expr instanceof AST\SimpleCaseExpression): + $columnAlias = $this->getSQLColumnAlias('sclr'); + $resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++; + + $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias; + + $this->_scalarResultAliasMap[$resultAlias] = $columnAlias; + + if ( ! $hidden) { + // We cannot resolve field type here; assume 'string'. + $this->_rsm->addScalarResult($columnAlias, $resultAlias, 'string'); + } + break; + + case ($expr instanceof AST\Subselect): + $columnAlias = $this->getSQLColumnAlias('sclr'); + $resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++; + + $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias; + + $this->_scalarResultAliasMap[$resultAlias] = $columnAlias; + + if ( ! $hidden) { + // We cannot resolve field type here; assume 'string'. + $this->_rsm->addScalarResult($columnAlias, $resultAlias, 'string'); + } + break; + + default: + // IdentificationVariable or PartialObjectExpression + if ($expr instanceof AST\PartialObjectExpression) { + $dqlAlias = $expr->identificationVariable; + $partialFieldSet = $expr->partialFieldSet; + } else { + $dqlAlias = $expr; + $partialFieldSet = array(); + } + + $queryComp = $this->_queryComponents[$dqlAlias]; + $class = $queryComp['metadata']; + $resultAlias = $selectExpression->fieldIdentificationVariable ?: null; + + if ( ! isset($this->_selectedClasses[$dqlAlias])) { + $this->_selectedClasses[$dqlAlias] = array( + 'class' => $class, + 'dqlAlias' => $dqlAlias, + 'resultAlias' => $resultAlias + ); + } + + $sqlParts = array(); + + // Select all fields from the queried class + foreach ($class->fieldMappings as $fieldName => $mapping) { + if ($partialFieldSet && ! in_array($fieldName, $partialFieldSet)) { + continue; + } + + $tableName = (isset($mapping['inherited'])) + ? $this->_em->getClassMetadata($mapping['inherited'])->getTableName() + : $class->getTableName(); + + $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias); + $columnAlias = $this->getSQLColumnAlias($mapping['columnName']); + $quotedColumnName = $class->getQuotedColumnName($fieldName, $this->_platform); + + $col = $sqlTableAlias . '.' . $quotedColumnName; + + if (isset($class->fieldMappings[$fieldName]['requireSQLConversion'])) { + $type = Type::getType($class->getTypeOfField($fieldName)); + $col = $type->convertToPHPValueSQL($col, $this->_platform); + } + + $sqlParts[] = $col . ' AS '. $columnAlias; + + $this->_scalarResultAliasMap[$resultAlias][] = $columnAlias; + + $this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name); + } + + // Add any additional fields of subclasses (excluding inherited fields) + // 1) on Single Table Inheritance: always, since its marginal overhead + // 2) on Class Table Inheritance only if partial objects are disallowed, + // since it requires outer joining subtables. + if ($class->isInheritanceTypeSingleTable() || ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { + foreach ($class->subClasses as $subClassName) { + $subClass = $this->_em->getClassMetadata($subClassName); + $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); + + foreach ($subClass->fieldMappings as $fieldName => $mapping) { + if (isset($mapping['inherited']) || $partialFieldSet && !in_array($fieldName, $partialFieldSet)) { + continue; + } + + $columnAlias = $this->getSQLColumnAlias($mapping['columnName']); + $quotedColumnName = $subClass->getQuotedColumnName($fieldName, $this->_platform); + + $col = $sqlTableAlias . '.' . $quotedColumnName; + + if (isset($subClass->fieldMappings[$fieldName]['requireSQLConversion'])) { + $type = Type::getType($subClass->getTypeOfField($fieldName)); + $col = $type->convertToPHPValueSQL($col, $this->_platform); + } + + $sqlParts[] = $col . ' AS ' . $columnAlias; + + $this->_scalarResultAliasMap[$resultAlias][] = $columnAlias; + + $this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName); + } + } + } + + $sql .= implode(', ', $sqlParts); + } + + return $sql; + } + + /** + * Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL. + * + * @param QuantifiedExpression + * @return string The SQL. + */ + public function walkQuantifiedExpression($qExpr) + { + return ' ' . strtoupper($qExpr->type) . '(' . $this->walkSubselect($qExpr->subselect) . ')'; + } + + /** + * Walks down a Subselect AST node, thereby generating the appropriate SQL. + * + * @param Subselect + * @return string The SQL. + */ + public function walkSubselect($subselect) + { + $useAliasesBefore = $this->_useSqlTableAliases; + $rootAliasesBefore = $this->_rootAliases; + + $this->_rootAliases = array(); // reset the rootAliases for the subselect + $this->_useSqlTableAliases = true; + + $sql = $this->walkSimpleSelectClause($subselect->simpleSelectClause); + $sql .= $this->walkSubselectFromClause($subselect->subselectFromClause); + $sql .= $this->walkWhereClause($subselect->whereClause); + + $sql .= $subselect->groupByClause ? $this->walkGroupByClause($subselect->groupByClause) : ''; + $sql .= $subselect->havingClause ? $this->walkHavingClause($subselect->havingClause) : ''; + $sql .= $subselect->orderByClause ? $this->walkOrderByClause($subselect->orderByClause) : ''; + + $this->_rootAliases = $rootAliasesBefore; // put the main aliases back + $this->_useSqlTableAliases = $useAliasesBefore; + + return $sql; + } + + /** + * Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL. + * + * @param SubselectFromClause + * @return string The SQL. + */ + public function walkSubselectFromClause($subselectFromClause) + { + $identificationVarDecls = $subselectFromClause->identificationVariableDeclarations; + $sqlParts = array (); + + foreach ($identificationVarDecls as $subselectIdVarDecl) { + $sql = ''; + + $rangeDecl = $subselectIdVarDecl->rangeVariableDeclaration; + $dqlAlias = $rangeDecl->aliasIdentificationVariable; + + $class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName); + $sql .= $class->getQuotedTableName($this->_platform) . ' ' + . $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + + $this->_rootAliases[] = $dqlAlias; + + if ($class->isInheritanceTypeJoined()) { + $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias); + } + + foreach ($subselectIdVarDecl->joinVariableDeclarations as $joinVarDecl) { + $sql .= $this->walkJoinVariableDeclaration($joinVarDecl); + } + + $sqlParts[] = $this->_platform->appendLockHint($sql, $this->_query->getHint(Query::HINT_LOCK_MODE)); + } + + return ' FROM ' . implode(', ', $sqlParts); + } + + /** + * Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectClause + * @return string The SQL. + */ + public function walkSimpleSelectClause($simpleSelectClause) + { + return 'SELECT' . ($simpleSelectClause->isDistinct ? ' DISTINCT' : '') + . $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression); + } + + /** + * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectExpression + * @return string The SQL. + */ + public function walkSimpleSelectExpression($simpleSelectExpression) + { + $expr = $simpleSelectExpression->expression; + $sql = ' '; + + switch (true) { + case ($expr instanceof AST\PathExpression): + $sql .= $this->walkPathExpression($expr); + break; + + case ($expr instanceof AST\AggregateExpression): + $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++; + + $sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias; + break; + + case ($expr instanceof AST\Subselect): + $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++; + + $columnAlias = 'sclr' . $this->_aliasCounter++; + $this->_scalarResultAliasMap[$alias] = $columnAlias; + + $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias; + break; + + case ($expr instanceof AST\Functions\FunctionNode): + case ($expr instanceof AST\SimpleArithmeticExpression): + case ($expr instanceof AST\ArithmeticTerm): + case ($expr instanceof AST\ArithmeticFactor): + case ($expr instanceof AST\ArithmeticPrimary): + case ($expr instanceof AST\Literal): + case ($expr instanceof AST\NullIfExpression): + case ($expr instanceof AST\CoalesceExpression): + case ($expr instanceof AST\GeneralCaseExpression): + case ($expr instanceof AST\SimpleCaseExpression): + $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++; + + $columnAlias = $this->getSQLColumnAlias('sclr'); + $this->_scalarResultAliasMap[$alias] = $columnAlias; + + $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias; + break; + + default: // IdentificationVariable + $sql .= $this->walkEntityIdentificationVariable($expr); + break; + } + + return $sql; + } + + /** + * Walks down an AggregateExpression AST node, thereby generating the appropriate SQL. + * + * @param AggregateExpression + * @return string The SQL. + */ + public function walkAggregateExpression($aggExpression) + { + return $aggExpression->functionName . '(' . ($aggExpression->isDistinct ? 'DISTINCT ' : '') + . $this->walkSimpleArithmeticExpression($aggExpression->pathExpression) . ')'; + } + + /** + * Walks down a GroupByClause AST node, thereby generating the appropriate SQL. + * + * @param GroupByClause + * @return string The SQL. + */ + public function walkGroupByClause($groupByClause) + { + $sqlParts = array(); + + foreach ($groupByClause->groupByItems AS $groupByItem) { + $sqlParts[] = $this->walkGroupByItem($groupByItem); + } + + return ' GROUP BY ' . implode(', ', $sqlParts); + } + + /** + * Walks down a GroupByItem AST node, thereby generating the appropriate SQL. + * + * @param GroupByItem + * @return string The SQL. + */ + public function walkGroupByItem($groupByItem) + { + // StateFieldPathExpression + if ( ! is_string($groupByItem)) { + return $this->walkPathExpression($groupByItem); + } + + // ResultVariable + if (isset($this->_queryComponents[$groupByItem]['resultVariable'])) { + return $this->walkResultVariable($groupByItem); + } + + // IdentificationVariable + $sqlParts = array(); + + foreach ($this->_queryComponents[$groupByItem]['metadata']->fieldNames AS $field) { + $item = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $field); + $item->type = AST\PathExpression::TYPE_STATE_FIELD; + + $sqlParts[] = $this->walkPathExpression($item); + } + + foreach ($this->_queryComponents[$groupByItem]['metadata']->associationMappings AS $mapping) { + if ($mapping['isOwningSide'] && $mapping['type'] & ClassMetadataInfo::TO_ONE) { + $item = new AST\PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $groupByItem, $mapping['fieldName']); + $item->type = AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; + + $sqlParts[] = $this->walkPathExpression($item); + } + } + + return implode(', ', $sqlParts); + } + + /** + * Walks down a DeleteClause AST node, thereby generating the appropriate SQL. + * + * @param DeleteClause + * @return string The SQL. + */ + public function walkDeleteClause(AST\DeleteClause $deleteClause) + { + $class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName); + $tableName = $class->getTableName(); + $sql = 'DELETE FROM ' . $class->getQuotedTableName($this->_platform); + + $this->setSQLTableAlias($tableName, $tableName, $deleteClause->aliasIdentificationVariable); + $this->_rootAliases[] = $deleteClause->aliasIdentificationVariable; + + return $sql; + } + + /** + * Walks down an UpdateClause AST node, thereby generating the appropriate SQL. + * + * @param UpdateClause + * @return string The SQL. + */ + public function walkUpdateClause($updateClause) + { + $class = $this->_em->getClassMetadata($updateClause->abstractSchemaName); + $tableName = $class->getTableName(); + $sql = 'UPDATE ' . $class->getQuotedTableName($this->_platform); + + $this->setSQLTableAlias($tableName, $tableName, $updateClause->aliasIdentificationVariable); + $this->_rootAliases[] = $updateClause->aliasIdentificationVariable; + + $sql .= ' SET ' . implode(', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems)); + + return $sql; + } + + /** + * Walks down an UpdateItem AST node, thereby generating the appropriate SQL. + * + * @param UpdateItem + * @return string The SQL. + */ + public function walkUpdateItem($updateItem) + { + $useTableAliasesBefore = $this->_useSqlTableAliases; + $this->_useSqlTableAliases = false; + + $sql = $this->walkPathExpression($updateItem->pathExpression) . ' = '; + $newValue = $updateItem->newValue; + + switch (true) { + case ($newValue instanceof AST\Node): + $sql .= $newValue->dispatch($this); + break; + + case ($newValue === null): + $sql .= 'NULL'; + break; + + default: + $sql .= $this->_conn->quote($newValue); + break; + } + + $this->_useSqlTableAliases = $useTableAliasesBefore; + + return $sql; + } + + /** + * Walks down a WhereClause AST node, thereby generating the appropriate SQL. + * WhereClause or not, the appropriate discriminator sql is added. + * + * @param WhereClause + * @return string The SQL. + */ + public function walkWhereClause($whereClause) + { + $condSql = null !== $whereClause ? $this->walkConditionalExpression($whereClause->conditionalExpression) : ''; + $discrSql = $this->_generateDiscriminatorColumnConditionSql($this->_rootAliases); + + if ($this->_em->hasFilters()) { + $filterClauses = array(); + foreach ($this->_rootAliases as $dqlAlias) { + $class = $this->_queryComponents[$dqlAlias]['metadata']; + $tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); + + if ($filterExpr = $this->generateFilterConditionSQL($class, $tableAlias)) { + $filterClauses[] = $filterExpr; + } + } + + if (count($filterClauses)) { + if ($condSql) { + $condSql .= ' AND '; + } + + $condSql .= implode(' AND ', $filterClauses); + } + } + + if ($condSql) { + return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql); + } + + if ($discrSql) { + return ' WHERE ' . $discrSql; + } + + return ''; + } + + /** + * Walk down a ConditionalExpression AST node, thereby generating the appropriate SQL. + * + * @param ConditionalExpression + * @return string The SQL. + */ + public function walkConditionalExpression($condExpr) + { + // Phase 2 AST optimization: Skip processment of ConditionalExpression + // if only one ConditionalTerm is defined + if ( ! ($condExpr instanceof AST\ConditionalExpression)) { + return $this->walkConditionalTerm($condExpr); + } + + return implode(' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms)); + } + + /** + * Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL. + * + * @param ConditionalTerm + * @return string The SQL. + */ + public function walkConditionalTerm($condTerm) + { + // Phase 2 AST optimization: Skip processment of ConditionalTerm + // if only one ConditionalFactor is defined + if ( ! ($condTerm instanceof AST\ConditionalTerm)) { + return $this->walkConditionalFactor($condTerm); + } + + return implode(' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors)); + } + + /** + * Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL. + * + * @param ConditionalFactor + * @return string The SQL. + */ + public function walkConditionalFactor($factor) + { + // Phase 2 AST optimization: Skip processment of ConditionalFactor + // if only one ConditionalPrimary is defined + return ( ! ($factor instanceof AST\ConditionalFactor)) + ? $this->walkConditionalPrimary($factor) + : ($factor->not ? 'NOT ' : '') . $this->walkConditionalPrimary($factor->conditionalPrimary); + } + + /** + * Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL. + * + * @param ConditionalPrimary + * @return string The SQL. + */ + public function walkConditionalPrimary($primary) + { + if ($primary->isSimpleConditionalExpression()) { + return $primary->simpleConditionalExpression->dispatch($this); + } + + if ($primary->isConditionalExpression()) { + $condExpr = $primary->conditionalExpression; + + return '(' . $this->walkConditionalExpression($condExpr) . ')'; + } + } + + /** + * Walks down an ExistsExpression AST node, thereby generating the appropriate SQL. + * + * @param ExistsExpression + * @return string The SQL. + */ + public function walkExistsExpression($existsExpr) + { + $sql = ($existsExpr->not) ? 'NOT ' : ''; + + $sql .= 'EXISTS (' . $this->walkSubselect($existsExpr->subselect) . ')'; + + return $sql; + } + + /** + * Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL. + * + * @param CollectionMemberExpression + * @return string The SQL. + */ + public function walkCollectionMemberExpression($collMemberExpr) + { + $sql = $collMemberExpr->not ? 'NOT ' : ''; + $sql .= 'EXISTS (SELECT 1 FROM '; + + $entityExpr = $collMemberExpr->entityExpression; + $collPathExpr = $collMemberExpr->collectionValuedPathExpression; + + $fieldName = $collPathExpr->field; + $dqlAlias = $collPathExpr->identificationVariable; + + $class = $this->_queryComponents[$dqlAlias]['metadata']; + + switch (true) { + // InputParameter + case ($entityExpr instanceof AST\InputParameter): + $dqlParamKey = $entityExpr->name; + $entity = $this->_query->getParameter($dqlParamKey); + $entitySql = '?'; + break; + + // SingleValuedAssociationPathExpression | IdentificationVariable + case ($entityExpr instanceof AST\PathExpression): + $entitySql = $this->walkPathExpression($entityExpr); + break; + + default: + throw new \BadMethodCallException("Not implemented"); + } + + $assoc = $class->associationMappings[$fieldName]; + + if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) { + $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); + $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName()); + $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + + $sql .= $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' WHERE '; + + $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']]; + $sqlParts = array(); + + foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) { + $targetColumn = $class->getQuotedColumnName($class->fieldNames[$targetColumn], $this->_platform); + + $sqlParts[] = $sourceTableAlias . '.' . $targetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn; + } + + foreach ($targetClass->getQuotedIdentifierColumnNames($this->_platform) as $targetColumnName) { + if (isset($dqlParamKey)) { + $this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++); + } + + $sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' = ' . $entitySql; + } + + $sql .= implode(' AND ', $sqlParts); + } else { // many-to-many + $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); + + $owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']]; + $joinTable = $owningAssoc['joinTable']; + + // SQL table aliases + $joinTableAlias = $this->getSQLTableAlias($joinTable['name']); + $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName()); + $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + + // join to target table + $sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $this->_platform) . ' ' . $joinTableAlias + . ' INNER JOIN ' . $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' ON '; + + // join conditions + $joinColumns = $assoc['isOwningSide'] ? $joinTable['inverseJoinColumns'] : $joinTable['joinColumns']; + $joinSqlParts = array(); + + foreach ($joinColumns as $joinColumn) { + $targetColumn = $targetClass->getQuotedColumnName( + $targetClass->fieldNames[$joinColumn['referencedColumnName']], + $this->_platform + ); + + $joinSqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $targetTableAlias . '.' . $targetColumn; + } + + $sql .= implode(' AND ', $joinSqlParts); + $sql .= ' WHERE '; + + $joinColumns = $assoc['isOwningSide'] ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns']; + $sqlParts = array(); + + foreach ($joinColumns as $joinColumn) { + $targetColumn = $class->getQuotedColumnName( + $class->fieldNames[$joinColumn['referencedColumnName']], + $this->_platform + ); + + $sqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $sourceTableAlias . '.' . $targetColumn; + } + + foreach ($targetClass->getQuotedIdentifierColumnNames($this->_platform) as $targetColumnName) { + if (isset($dqlParamKey)) { + $this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++); + } + + $sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' = ' . $entitySql; + } + + $sql .= implode(' AND ', $sqlParts); + } + + return $sql . ')'; + } + + /** + * Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param EmptyCollectionComparisonExpression + * @return string The SQL. + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) + { + $sizeFunc = new AST\Functions\SizeFunction('size'); + $sizeFunc->collectionPathExpression = $emptyCollCompExpr->expression; + + return $sizeFunc->getSql($this) . ($emptyCollCompExpr->not ? ' > 0' : ' = 0'); + } + + /** + * Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param NullComparisonExpression + * @return string The SQL. + */ + public function walkNullComparisonExpression($nullCompExpr) + { + $sql = ''; + $innerExpr = $nullCompExpr->expression; + + if ($innerExpr instanceof AST\InputParameter) { + $dqlParamKey = $innerExpr->name; + $this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++); + $sql .= ' ?'; + } else { + $sql .= $this->walkPathExpression($innerExpr); + } + + $sql .= ' IS' . ($nullCompExpr->not ? ' NOT' : '') . ' NULL'; + + return $sql; + } + + /** + * Walks down an InExpression AST node, thereby generating the appropriate SQL. + * + * @param InExpression + * @return string The SQL. + */ + public function walkInExpression($inExpr) + { + $sql = $this->walkArithmeticExpression($inExpr->expression) . ($inExpr->not ? ' NOT' : '') . ' IN ('; + + $sql .= ($inExpr->subselect) + ? $this->walkSubselect($inExpr->subselect) + : implode(', ', array_map(array($this, 'walkInParameter'), $inExpr->literals)); + + $sql .= ')'; + + return $sql; + } + + /** + * Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL. + * + * @param InstanceOfExpression + * @return string The SQL. + */ + public function walkInstanceOfExpression($instanceOfExpr) + { + $sql = ''; + + $dqlAlias = $instanceOfExpr->identificationVariable; + $discrClass = $class = $this->_queryComponents[$dqlAlias]['metadata']; + $fieldName = null; + + if ($class->discriminatorColumn) { + $discrClass = $this->_em->getClassMetadata($class->rootEntityName); + } + + if ($this->_useSqlTableAliases) { + $sql .= $this->getSQLTableAlias($discrClass->getTableName(), $dqlAlias) . '.'; + } + + $sql .= $class->discriminatorColumn['name'] . ($instanceOfExpr->not ? ' NOT IN ' : ' IN '); + + $sqlParameterList = array(); + + foreach ($instanceOfExpr->value as $parameter) { + if ($parameter instanceof AST\InputParameter) { + // We need to modify the parameter value to be its correspondent mapped value + $dqlParamKey = $parameter->name; + $paramValue = $this->_query->getParameter($dqlParamKey); + + if ( ! ($paramValue instanceof \Doctrine\ORM\Mapping\ClassMetadata)) { + throw QueryException::invalidParameterType('ClassMetadata', get_class($paramValue)); + } + + $entityClassName = $paramValue->name; + } else { + // Get name from ClassMetadata to resolve aliases. + $entityClassName = $this->_em->getClassMetadata($parameter)->name; + } + + if ($entityClassName == $class->name) { + $sqlParameterList[] = $this->_conn->quote($class->discriminatorValue); + } else { + $discrMap = array_flip($class->discriminatorMap); + + if (!isset($discrMap[$entityClassName])) { + throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName); + } + + $sqlParameterList[] = $this->_conn->quote($discrMap[$entityClassName]); + } + } + + $sql .= '(' . implode(', ', $sqlParameterList) . ')'; + + return $sql; + } + + /** + * Walks down an InParameter AST node, thereby generating the appropriate SQL. + * + * @param InParameter + * @return string The SQL. + */ + public function walkInParameter($inParam) + { + return $inParam instanceof AST\InputParameter + ? $this->walkInputParameter($inParam) + : $this->walkLiteral($inParam); + } + + /** + * Walks down a literal that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkLiteral($literal) + { + switch ($literal->type) { + case AST\Literal::STRING: + return $this->_conn->quote($literal->value); + + case AST\Literal::BOOLEAN: + $bool = strtolower($literal->value) == 'true' ? true : false; + $boolVal = $this->_conn->getDatabasePlatform()->convertBooleans($bool); + + return $boolVal; + + case AST\Literal::NUMERIC: + return $literal->value; + + default: + throw QueryException::invalidLiteral($literal); + } + } + + /** + * Walks down a BetweenExpression AST node, thereby generating the appropriate SQL. + * + * @param BetweenExpression + * @return string The SQL. + */ + public function walkBetweenExpression($betweenExpr) + { + $sql = $this->walkArithmeticExpression($betweenExpr->expression); + + if ($betweenExpr->not) $sql .= ' NOT'; + + $sql .= ' BETWEEN ' . $this->walkArithmeticExpression($betweenExpr->leftBetweenExpression) + . ' AND ' . $this->walkArithmeticExpression($betweenExpr->rightBetweenExpression); + + return $sql; + } + + /** + * Walks down a LikeExpression AST node, thereby generating the appropriate SQL. + * + * @param LikeExpression + * @return string The SQL. + */ + public function walkLikeExpression($likeExpr) + { + $stringExpr = $likeExpr->stringExpression; + $sql = $stringExpr->dispatch($this) . ($likeExpr->not ? ' NOT' : '') . ' LIKE '; + + if ($likeExpr->stringPattern instanceof AST\InputParameter) { + $inputParam = $likeExpr->stringPattern; + $dqlParamKey = $inputParam->name; + $this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++); + $sql .= '?'; + } else { + $sql .= $this->_conn->quote($likeExpr->stringPattern); + } + + if ($likeExpr->escapeChar) { + $sql .= ' ESCAPE ' . $this->_conn->quote($likeExpr->escapeChar); + } + + return $sql; + } + + /** + * Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL. + * + * @param StateFieldPathExpression + * @return string The SQL. + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) + { + return $this->walkPathExpression($stateFieldPathExpression); + } + + /** + * Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param ComparisonExpression + * @return string The SQL. + */ + public function walkComparisonExpression($compExpr) + { + $leftExpr = $compExpr->leftExpression; + $rightExpr = $compExpr->rightExpression; + $sql = ''; + + $sql .= ($leftExpr instanceof AST\Node) + ? $leftExpr->dispatch($this) + : (is_numeric($leftExpr) ? $leftExpr : $this->_conn->quote($leftExpr)); + + $sql .= ' ' . $compExpr->operator . ' '; + + $sql .= ($rightExpr instanceof AST\Node) + ? $rightExpr->dispatch($this) + : (is_numeric($rightExpr) ? $rightExpr : $this->_conn->quote($rightExpr)); + + return $sql; + } + + /** + * Walks down an InputParameter AST node, thereby generating the appropriate SQL. + * + * @param InputParameter + * @return string The SQL. + */ + public function walkInputParameter($inputParam) + { + $this->_parserResult->addParameterMapping($inputParam->name, $this->_sqlParamIndex++); + + return '?'; + } + + /** + * Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param ArithmeticExpression + * @return string The SQL. + */ + public function walkArithmeticExpression($arithmeticExpr) + { + return ($arithmeticExpr->isSimpleArithmeticExpression()) + ? $this->walkSimpleArithmeticExpression($arithmeticExpr->simpleArithmeticExpression) + : '(' . $this->walkSubselect($arithmeticExpr->subselect) . ')'; + } + + /** + * Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleArithmeticExpression + * @return string The SQL. + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) + { + if ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) { + return $this->walkArithmeticTerm($simpleArithmeticExpr); + } + + return implode(' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms)); + } + + /** + * Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkArithmeticTerm($term) + { + if (is_string($term)) { + return (isset($this->_queryComponents[$term])) + ? $this->walkResultVariable($this->_queryComponents[$term]['token']['value']) + : $term; + } + + // Phase 2 AST optimization: Skip processment of ArithmeticTerm + // if only one ArithmeticFactor is defined + if ( ! ($term instanceof AST\ArithmeticTerm)) { + return $this->walkArithmeticFactor($term); + } + + return implode(' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors)); + } + + /** + * Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkArithmeticFactor($factor) + { + if (is_string($factor)) { + return $factor; + } + + // Phase 2 AST optimization: Skip processment of ArithmeticFactor + // if only one ArithmeticPrimary is defined + if ( ! ($factor instanceof AST\ArithmeticFactor)) { + return $this->walkArithmeticPrimary($factor); + } + + $sign = $factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : ''); + + return $sign . $this->walkArithmeticPrimary($factor->arithmeticPrimary); + } + + /** + * Walks down an ArithmeticPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkArithmeticPrimary($primary) + { + if ($primary instanceof AST\SimpleArithmeticExpression) { + return '(' . $this->walkSimpleArithmeticExpression($primary) . ')'; + } + + if ($primary instanceof AST\Node) { + return $primary->dispatch($this); + } + + return $this->walkEntityIdentificationVariable($primary); + } + + /** + * Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkStringPrimary($stringPrimary) + { + return (is_string($stringPrimary)) + ? $this->_conn->quote($stringPrimary) + : $stringPrimary->dispatch($this); + } + + /** + * Walks down a ResultVriable that represents an AST node, thereby generating the appropriate SQL. + * + * @param string $resultVariable + * @return string The SQL. + */ + public function walkResultVariable($resultVariable) + { + $resultAlias = $this->_scalarResultAliasMap[$resultVariable]; + + if (is_array($resultAlias)) { + return implode(', ', $resultAlias); + } + + return $resultAlias; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalker.php new file mode 100644 index 0000000..8462898 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalker.php @@ -0,0 +1,411 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Interface for walkers of DQL ASTs (abstract syntax trees). + * + * @author Roman Borschel + * @since 2.0 + */ +interface TreeWalker +{ + /** + * Initializes TreeWalker with important information about the ASTs to be walked + * + * @param Query $query The parsed Query. + * @param ParserResult $parserResult The result of the parsing process. + * @param array $queryComponents Query components (symbol table) + */ + public function __construct($query, $parserResult, array $queryComponents); + + /** + * Walks down a SelectStatement AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + function walkSelectStatement(AST\SelectStatement $AST); + + /** + * Walks down a SelectClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + function walkSelectClause($selectClause); + + /** + * Walks down a FromClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + function walkFromClause($fromClause); + + /** + * Walks down a FunctionNode AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + function walkFunction($function); + + /** + * Walks down an OrderByClause AST node, thereby generating the appropriate SQL. + * + * @param OrderByClause + * @return string The SQL. + */ + function walkOrderByClause($orderByClause); + + /** + * Walks down an OrderByItem AST node, thereby generating the appropriate SQL. + * + * @param OrderByItem + * @return string The SQL. + */ + function walkOrderByItem($orderByItem); + + /** + * Walks down a HavingClause AST node, thereby generating the appropriate SQL. + * + * @param HavingClause + * @return string The SQL. + */ + function walkHavingClause($havingClause); + + /** + * Walks down a JoinVariableDeclaration AST node and creates the corresponding SQL. + * + * @param JoinVariableDeclaration $joinVarDecl + * @return string The SQL. + */ + function walkJoinVariableDeclaration($joinVarDecl); + + /** + * Walks down a SelectExpression AST node and generates the corresponding SQL. + * + * @param SelectExpression $selectExpression + * @return string The SQL. + */ + function walkSelectExpression($selectExpression); + + /** + * Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL. + * + * @param QuantifiedExpression + * @return string The SQL. + */ + function walkQuantifiedExpression($qExpr); + + /** + * Walks down a Subselect AST node, thereby generating the appropriate SQL. + * + * @param Subselect + * @return string The SQL. + */ + function walkSubselect($subselect); + + /** + * Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL. + * + * @param SubselectFromClause + * @return string The SQL. + */ + function walkSubselectFromClause($subselectFromClause); + + /** + * Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectClause + * @return string The SQL. + */ + function walkSimpleSelectClause($simpleSelectClause); + + /** + * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectExpression + * @return string The SQL. + */ + function walkSimpleSelectExpression($simpleSelectExpression); + + /** + * Walks down an AggregateExpression AST node, thereby generating the appropriate SQL. + * + * @param AggregateExpression + * @return string The SQL. + */ + function walkAggregateExpression($aggExpression); + + /** + * Walks down a GroupByClause AST node, thereby generating the appropriate SQL. + * + * @param GroupByClause + * @return string The SQL. + */ + function walkGroupByClause($groupByClause); + + /** + * Walks down a GroupByItem AST node, thereby generating the appropriate SQL. + * + * @param GroupByItem + * @return string The SQL. + */ + function walkGroupByItem($groupByItem); + + /** + * Walks down an UpdateStatement AST node, thereby generating the appropriate SQL. + * + * @param UpdateStatement + * @return string The SQL. + */ + function walkUpdateStatement(AST\UpdateStatement $AST); + + /** + * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL. + * + * @param DeleteStatement + * @return string The SQL. + */ + function walkDeleteStatement(AST\DeleteStatement $AST); + + /** + * Walks down a DeleteClause AST node, thereby generating the appropriate SQL. + * + * @param DeleteClause + * @return string The SQL. + */ + function walkDeleteClause(AST\DeleteClause $deleteClause); + + /** + * Walks down an UpdateClause AST node, thereby generating the appropriate SQL. + * + * @param UpdateClause + * @return string The SQL. + */ + function walkUpdateClause($updateClause); + + /** + * Walks down an UpdateItem AST node, thereby generating the appropriate SQL. + * + * @param UpdateItem + * @return string The SQL. + */ + function walkUpdateItem($updateItem); + + /** + * Walks down a WhereClause AST node, thereby generating the appropriate SQL. + * + * @param WhereClause + * @return string The SQL. + */ + function walkWhereClause($whereClause); + + /** + * Walks down a ConditionalExpression AST node, thereby generating the appropriate SQL. + * + * @param ConditionalExpression + * @return string The SQL. + */ + function walkConditionalExpression($condExpr); + + /** + * Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL. + * + * @param ConditionalTerm + * @return string The SQL. + */ + function walkConditionalTerm($condTerm); + + /** + * Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL. + * + * @param ConditionalFactor + * @return string The SQL. + */ + function walkConditionalFactor($factor); + + /** + * Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL. + * + * @param ConditionalPrimary + * @return string The SQL. + */ + function walkConditionalPrimary($primary); + + /** + * Walks down an ExistsExpression AST node, thereby generating the appropriate SQL. + * + * @param ExistsExpression + * @return string The SQL. + */ + function walkExistsExpression($existsExpr); + + /** + * Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL. + * + * @param CollectionMemberExpression + * @return string The SQL. + */ + function walkCollectionMemberExpression($collMemberExpr); + + /** + * Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param EmptyCollectionComparisonExpression + * @return string The SQL. + */ + function walkEmptyCollectionComparisonExpression($emptyCollCompExpr); + + /** + * Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param NullComparisonExpression + * @return string The SQL. + */ + function walkNullComparisonExpression($nullCompExpr); + + /** + * Walks down an InExpression AST node, thereby generating the appropriate SQL. + * + * @param InExpression + * @return string The SQL. + */ + function walkInExpression($inExpr); + + /** + * Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL. + * + * @param InstanceOfExpression + * @return string The SQL. + */ + function walkInstanceOfExpression($instanceOfExpr); + + /** + * Walks down a literal that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + function walkLiteral($literal); + + /** + * Walks down a BetweenExpression AST node, thereby generating the appropriate SQL. + * + * @param BetweenExpression + * @return string The SQL. + */ + function walkBetweenExpression($betweenExpr); + + /** + * Walks down a LikeExpression AST node, thereby generating the appropriate SQL. + * + * @param LikeExpression + * @return string The SQL. + */ + function walkLikeExpression($likeExpr); + + /** + * Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL. + * + * @param StateFieldPathExpression + * @return string The SQL. + */ + function walkStateFieldPathExpression($stateFieldPathExpression); + + /** + * Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param ComparisonExpression + * @return string The SQL. + */ + function walkComparisonExpression($compExpr); + + /** + * Walks down an InputParameter AST node, thereby generating the appropriate SQL. + * + * @param InputParameter + * @return string The SQL. + */ + function walkInputParameter($inputParam); + + /** + * Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param ArithmeticExpression + * @return string The SQL. + */ + function walkArithmeticExpression($arithmeticExpr); + + /** + * Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + function walkArithmeticTerm($term); + + /** + * Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + function walkStringPrimary($stringPrimary); + + /** + * Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + function walkArithmeticFactor($factor); + + /** + * Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleArithmeticExpression + * @return string The SQL. + */ + function walkSimpleArithmeticExpression($simpleArithmeticExpr); + + /** + * Walks down an PathExpression AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + function walkPathExpression($pathExpr); + + /** + * Walks down an ResultVariable AST node, thereby generating the appropriate SQL. + * + * @param string $resultVariable + * @return string The SQL. + */ + function walkResultVariable($resultVariable); + + /** + * Gets an executor that can be used to execute the result of this walker. + * + * @return AbstractExecutor + */ + function getExecutor($AST); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php new file mode 100644 index 0000000..5907aa9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php @@ -0,0 +1,445 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * An adapter implementation of the TreeWalker interface. The methods in this class + * are empty. This class exists as convenience for creating tree walkers. + * + * @author Roman Borschel + * @since 2.0 + */ +abstract class TreeWalkerAdapter implements TreeWalker +{ + private $_query; + private $_parserResult; + private $_queryComponents; + + /** + * {@inheritdoc} + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->_query = $query; + $this->_parserResult = $parserResult; + $this->_queryComponents = $queryComponents; + } + + /** + * @return array + */ + protected function _getQueryComponents() + { + return $this->_queryComponents; + } + + /** + * Retrieve Query Instance reponsible for the current walkers execution. + * + * @return \Doctrine\ORM\Query + */ + protected function _getQuery() + { + return $this->_query; + } + + /** + * Retrieve ParserResult + * + * @return \Doctrine\ORM\Query\ParserResult + */ + protected function _getParserResult() + { + return $this->_parserResult; + } + + /** + * Walks down a SelectStatement AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkSelectStatement(AST\SelectStatement $AST) {} + + /** + * Walks down a SelectClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkSelectClause($selectClause) {} + + /** + * Walks down a FromClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkFromClause($fromClause) {} + + /** + * Walks down a FunctionNode AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkFunction($function) {} + + /** + * Walks down an OrderByClause AST node, thereby generating the appropriate SQL. + * + * @param OrderByClause + * @return string The SQL. + */ + public function walkOrderByClause($orderByClause) {} + + /** + * Walks down an OrderByItem AST node, thereby generating the appropriate SQL. + * + * @param OrderByItem + * @return string The SQL. + */ + public function walkOrderByItem($orderByItem) {} + + /** + * Walks down a HavingClause AST node, thereby generating the appropriate SQL. + * + * @param HavingClause + * @return string The SQL. + */ + public function walkHavingClause($havingClause) {} + + /** + * Walks down a JoinVariableDeclaration AST node and creates the corresponding SQL. + * + * @param JoinVariableDeclaration $joinVarDecl + * @return string The SQL. + */ + public function walkJoinVariableDeclaration($joinVarDecl) {} + + /** + * Walks down a SelectExpression AST node and generates the corresponding SQL. + * + * @param SelectExpression $selectExpression + * @return string The SQL. + */ + public function walkSelectExpression($selectExpression) {} + + /** + * Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL. + * + * @param QuantifiedExpression + * @return string The SQL. + */ + public function walkQuantifiedExpression($qExpr) {} + + /** + * Walks down a Subselect AST node, thereby generating the appropriate SQL. + * + * @param Subselect + * @return string The SQL. + */ + public function walkSubselect($subselect) {} + + /** + * Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL. + * + * @param SubselectFromClause + * @return string The SQL. + */ + public function walkSubselectFromClause($subselectFromClause) {} + + /** + * Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectClause + * @return string The SQL. + */ + public function walkSimpleSelectClause($simpleSelectClause) {} + + /** + * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectExpression + * @return string The SQL. + */ + public function walkSimpleSelectExpression($simpleSelectExpression) {} + + /** + * Walks down an AggregateExpression AST node, thereby generating the appropriate SQL. + * + * @param AggregateExpression + * @return string The SQL. + */ + public function walkAggregateExpression($aggExpression) {} + + /** + * Walks down a GroupByClause AST node, thereby generating the appropriate SQL. + * + * @param GroupByClause + * @return string The SQL. + */ + public function walkGroupByClause($groupByClause) {} + + /** + * Walks down a GroupByItem AST node, thereby generating the appropriate SQL. + * + * @param GroupByItem + * @return string The SQL. + */ + public function walkGroupByItem($groupByItem) {} + + /** + * Walks down an UpdateStatement AST node, thereby generating the appropriate SQL. + * + * @param UpdateStatement + * @return string The SQL. + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) {} + + /** + * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL. + * + * @param DeleteStatement + * @return string The SQL. + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) {} + + /** + * Walks down a DeleteClause AST node, thereby generating the appropriate SQL. + * + * @param DeleteClause + * @return string The SQL. + */ + public function walkDeleteClause(AST\DeleteClause $deleteClause) {} + + /** + * Walks down an UpdateClause AST node, thereby generating the appropriate SQL. + * + * @param UpdateClause + * @return string The SQL. + */ + public function walkUpdateClause($updateClause) {} + + /** + * Walks down an UpdateItem AST node, thereby generating the appropriate SQL. + * + * @param UpdateItem + * @return string The SQL. + */ + public function walkUpdateItem($updateItem) {} + + /** + * Walks down a WhereClause AST node, thereby generating the appropriate SQL. + * + * @param WhereClause + * @return string The SQL. + */ + public function walkWhereClause($whereClause) {} + + /** + * Walks down a ConditionalExpression AST node, thereby generating the appropriate SQL. + * + * @param ConditionalExpression + * @return string The SQL. + */ + public function walkConditionalExpression($condExpr) {} + + /** + * Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL. + * + * @param ConditionalTerm + * @return string The SQL. + */ + public function walkConditionalTerm($condTerm) {} + + /** + * Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL. + * + * @param ConditionalFactor + * @return string The SQL. + */ + public function walkConditionalFactor($factor) {} + + /** + * Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL. + * + * @param ConditionalPrimary + * @return string The SQL. + */ + public function walkConditionalPrimary($primary) {} + + /** + * Walks down an ExistsExpression AST node, thereby generating the appropriate SQL. + * + * @param ExistsExpression + * @return string The SQL. + */ + public function walkExistsExpression($existsExpr) {} + + /** + * Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL. + * + * @param CollectionMemberExpression + * @return string The SQL. + */ + public function walkCollectionMemberExpression($collMemberExpr) {} + + /** + * Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param EmptyCollectionComparisonExpression + * @return string The SQL. + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) {} + + /** + * Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param NullComparisonExpression + * @return string The SQL. + */ + public function walkNullComparisonExpression($nullCompExpr) {} + + /** + * Walks down an InExpression AST node, thereby generating the appropriate SQL. + * + * @param InExpression + * @return string The SQL. + */ + public function walkInExpression($inExpr) {} + + /** + * Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL. + * + * @param InstanceOfExpression + * @return string The SQL. + */ + function walkInstanceOfExpression($instanceOfExpr) {} + + /** + * Walks down a literal that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkLiteral($literal) {} + + /** + * Walks down a BetweenExpression AST node, thereby generating the appropriate SQL. + * + * @param BetweenExpression + * @return string The SQL. + */ + public function walkBetweenExpression($betweenExpr) {} + + /** + * Walks down a LikeExpression AST node, thereby generating the appropriate SQL. + * + * @param LikeExpression + * @return string The SQL. + */ + public function walkLikeExpression($likeExpr) {} + + /** + * Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL. + * + * @param StateFieldPathExpression + * @return string The SQL. + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) {} + + /** + * Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param ComparisonExpression + * @return string The SQL. + */ + public function walkComparisonExpression($compExpr) {} + + /** + * Walks down an InputParameter AST node, thereby generating the appropriate SQL. + * + * @param InputParameter + * @return string The SQL. + */ + public function walkInputParameter($inputParam) {} + + /** + * Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param ArithmeticExpression + * @return string The SQL. + */ + public function walkArithmeticExpression($arithmeticExpr) {} + + /** + * Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkArithmeticTerm($term) {} + + /** + * Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkStringPrimary($stringPrimary) {} + + /** + * Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkArithmeticFactor($factor) {} + + /** + * Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleArithmeticExpression + * @return string The SQL. + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) {} + + /** + * Walks down an PathExpression AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkPathExpression($pathExpr) {} + + /** + * Walks down an ResultVariable AST node, thereby generating the appropriate SQL. + * + * @param string $resultVariable + * @return string The SQL. + */ + public function walkResultVariable($resultVariable) {} + + /** + * Gets an executor that can be used to execute the result of this walker. + * + * @return AbstractExecutor + */ + public function getExecutor($AST) {} +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChain.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChain.php new file mode 100644 index 0000000..d3891a1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChain.php @@ -0,0 +1,664 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Represents a chain of tree walkers that modify an AST and finally emit output. + * Only the last walker in the chain can emit output. Any previous walkers can modify + * the AST to influence the final output produced by the last walker. + * + * @author Roman Borschel + * @since 2.0 + */ +class TreeWalkerChain implements TreeWalker +{ + /** The tree walkers. */ + private $_walkers = array(); + /** The original Query. */ + private $_query; + /** The ParserResult of the original query that was produced by the Parser. */ + private $_parserResult; + /** The query components of the original query (the "symbol table") that was produced by the Parser. */ + private $_queryComponents; + + /** + * @inheritdoc + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->_query = $query; + $this->_parserResult = $parserResult; + $this->_queryComponents = $queryComponents; + } + + /** + * Adds a tree walker to the chain. + * + * @param string $walkerClass The class of the walker to instantiate. + */ + public function addTreeWalker($walkerClass) + { + $this->_walkers[] = new $walkerClass($this->_query, $this->_parserResult, $this->_queryComponents); + } + + /** + * Walks down a SelectStatement AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkSelectStatement(AST\SelectStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectStatement($AST); + } + } + + /** + * Walks down a SelectClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkSelectClause($selectClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectClause($selectClause); + } + } + + /** + * Walks down a FromClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkFromClause($fromClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkFromClause($fromClause); + } + } + + /** + * Walks down a FunctionNode AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ + public function walkFunction($function) + { + foreach ($this->_walkers as $walker) { + $walker->walkFunction($function); + } + } + + /** + * Walks down an OrderByClause AST node, thereby generating the appropriate SQL. + * + * @param OrderByClause + * @return string The SQL. + */ + public function walkOrderByClause($orderByClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkOrderByClause($orderByClause); + } + } + + /** + * Walks down an OrderByItem AST node, thereby generating the appropriate SQL. + * + * @param OrderByItem + * @return string The SQL. + */ + public function walkOrderByItem($orderByItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkOrderByItem($orderByItem); + } + } + + /** + * Walks down a HavingClause AST node, thereby generating the appropriate SQL. + * + * @param HavingClause + * @return string The SQL. + */ + public function walkHavingClause($havingClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkHavingClause($havingClause); + } + } + + /** + * Walks down a JoinVariableDeclaration AST node and creates the corresponding SQL. + * + * @param JoinVariableDeclaration $joinVarDecl + * @return string The SQL. + */ + public function walkJoinVariableDeclaration($joinVarDecl) + { + foreach ($this->_walkers as $walker) { + $walker->walkJoinVariableDeclaration($joinVarDecl); + } + } + + /** + * Walks down a SelectExpression AST node and generates the corresponding SQL. + * + * @param SelectExpression $selectExpression + * @return string The SQL. + */ + public function walkSelectExpression($selectExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectExpression($selectExpression); + } + } + + /** + * Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL. + * + * @param QuantifiedExpression + * @return string The SQL. + */ + public function walkQuantifiedExpression($qExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkQuantifiedExpression($qExpr); + } + } + + /** + * Walks down a Subselect AST node, thereby generating the appropriate SQL. + * + * @param Subselect + * @return string The SQL. + */ + public function walkSubselect($subselect) + { + foreach ($this->_walkers as $walker) { + $walker->walkSubselect($subselect); + } + } + + /** + * Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL. + * + * @param SubselectFromClause + * @return string The SQL. + */ + public function walkSubselectFromClause($subselectFromClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSubselectFromClause($subselectFromClause); + } + } + + /** + * Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectClause + * @return string The SQL. + */ + public function walkSimpleSelectClause($simpleSelectClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleSelectClause($simpleSelectClause); + } + } + + /** + * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectExpression + * @return string The SQL. + */ + public function walkSimpleSelectExpression($simpleSelectExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleSelectExpression($simpleSelectExpression); + } + } + + /** + * Walks down an AggregateExpression AST node, thereby generating the appropriate SQL. + * + * @param AggregateExpression + * @return string The SQL. + */ + public function walkAggregateExpression($aggExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkAggregateExpression($aggExpression); + } + } + + /** + * Walks down a GroupByClause AST node, thereby generating the appropriate SQL. + * + * @param GroupByClause + * @return string The SQL. + */ + public function walkGroupByClause($groupByClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkGroupByClause($groupByClause); + } + } + + /** + * Walks down a GroupByItem AST node, thereby generating the appropriate SQL. + * + * @param GroupByItem + * @return string The SQL. + */ + public function walkGroupByItem($groupByItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkGroupByItem($groupByItem); + } + } + + /** + * Walks down an UpdateStatement AST node, thereby generating the appropriate SQL. + * + * @param UpdateStatement + * @return string The SQL. + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateStatement($AST); + } + } + + /** + * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL. + * + * @param DeleteStatement + * @return string The SQL. + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkDeleteStatement($AST); + } + } + + /** + * Walks down a DeleteClause AST node, thereby generating the appropriate SQL. + * + * @param DeleteClause + * @return string The SQL. + */ + public function walkDeleteClause(AST\DeleteClause $deleteClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkDeleteClause($deleteClause); + } + } + + /** + * Walks down an UpdateClause AST node, thereby generating the appropriate SQL. + * + * @param UpdateClause + * @return string The SQL. + */ + public function walkUpdateClause($updateClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateClause($updateClause); + } + } + + /** + * Walks down an UpdateItem AST node, thereby generating the appropriate SQL. + * + * @param UpdateItem + * @return string The SQL. + */ + public function walkUpdateItem($updateItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateItem($updateItem); + } + } + + /** + * Walks down a WhereClause AST node, thereby generating the appropriate SQL. + * + * @param WhereClause + * @return string The SQL. + */ + public function walkWhereClause($whereClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkWhereClause($whereClause); + } + } + + /** + * Walks down a ConditionalExpression AST node, thereby generating the appropriate SQL. + * + * @param ConditionalExpression + * @return string The SQL. + */ + public function walkConditionalExpression($condExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalExpression($condExpr); + } + } + + /** + * Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL. + * + * @param ConditionalTerm + * @return string The SQL. + */ + public function walkConditionalTerm($condTerm) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalTerm($condTerm); + } + } + + /** + * Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL. + * + * @param ConditionalFactor + * @return string The SQL. + */ + public function walkConditionalFactor($factor) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalFactor($factor); + } + } + + /** + * Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL. + * + * @param ConditionalPrimary + * @return string The SQL. + */ + public function walkConditionalPrimary($condPrimary) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalPrimary($condPrimary); + } + } + + /** + * Walks down an ExistsExpression AST node, thereby generating the appropriate SQL. + * + * @param ExistsExpression + * @return string The SQL. + */ + public function walkExistsExpression($existsExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkExistsExpression($existsExpr); + } + } + + /** + * Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL. + * + * @param CollectionMemberExpression + * @return string The SQL. + */ + public function walkCollectionMemberExpression($collMemberExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkCollectionMemberExpression($collMemberExpr); + } + } + + /** + * Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param EmptyCollectionComparisonExpression + * @return string The SQL. + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkEmptyCollectionComparisonExpression($emptyCollCompExpr); + } + } + + /** + * Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param NullComparisonExpression + * @return string The SQL. + */ + public function walkNullComparisonExpression($nullCompExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkNullComparisonExpression($nullCompExpr); + } + } + + /** + * Walks down an InExpression AST node, thereby generating the appropriate SQL. + * + * @param InExpression + * @return string The SQL. + */ + public function walkInExpression($inExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkInExpression($inExpr); + } + } + + /** + * Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL. + * + * @param InstanceOfExpression + * @return string The SQL. + */ + function walkInstanceOfExpression($instanceOfExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkInstanceOfExpression($instanceOfExpr); + } + } + + /** + * Walks down a literal that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkLiteral($literal) + { + foreach ($this->_walkers as $walker) { + $walker->walkLiteral($literal); + } + } + + /** + * Walks down a BetweenExpression AST node, thereby generating the appropriate SQL. + * + * @param BetweenExpression + * @return string The SQL. + */ + public function walkBetweenExpression($betweenExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkBetweenExpression($betweenExpr); + } + } + + /** + * Walks down a LikeExpression AST node, thereby generating the appropriate SQL. + * + * @param LikeExpression + * @return string The SQL. + */ + public function walkLikeExpression($likeExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkLikeExpression($likeExpr); + } + } + + /** + * Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL. + * + * @param StateFieldPathExpression + * @return string The SQL. + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkStateFieldPathExpression($stateFieldPathExpression); + } + } + + /** + * Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param ComparisonExpression + * @return string The SQL. + */ + public function walkComparisonExpression($compExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkComparisonExpression($compExpr); + } + } + + /** + * Walks down an InputParameter AST node, thereby generating the appropriate SQL. + * + * @param InputParameter + * @return string The SQL. + */ + public function walkInputParameter($inputParam) + { + foreach ($this->_walkers as $walker) { + $walker->walkInputParameter($inputParam); + } + } + + /** + * Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param ArithmeticExpression + * @return string The SQL. + */ + public function walkArithmeticExpression($arithmeticExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticExpression($arithmeticExpr); + } + } + + /** + * Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkArithmeticTerm($term) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticTerm($term); + } + } + + /** + * Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkStringPrimary($stringPrimary) + { + foreach ($this->_walkers as $walker) { + $walker->walkStringPrimary($stringPrimary); + } + } + + /** + * Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkArithmeticFactor($factor) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticFactor($factor); + } + } + + /** + * Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleArithmeticExpression + * @return string The SQL. + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleArithmeticExpression($simpleArithmeticExpr); + } + } + + /** + * Walks down an PathExpression AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ + public function walkPathExpression($pathExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkPathExpression($pathExpr); + } + } + + /** + * Walks down an ResultVariable AST node, thereby generating the appropriate SQL. + * + * @param string $resultVariable + * @return string The SQL. + */ + public function walkResultVariable($resultVariable) + { + foreach ($this->_walkers as $walker) { + $walker->walkResultVariable($resultVariable); + } + } + + /** + * Gets an executor that can be used to execute the result of this walker. + * + * @return AbstractExecutor + */ + public function getExecutor($AST) + {} +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php new file mode 100644 index 0000000..5212634 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php @@ -0,0 +1,1115 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\ORM\Query\Expr; + +/** + * This class is responsible for building DQL query strings via an object oriented + * PHP interface. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class QueryBuilder +{ + /* The query types. */ + const SELECT = 0; + const DELETE = 1; + const UPDATE = 2; + + /** The builder states. */ + const STATE_DIRTY = 0; + const STATE_CLEAN = 1; + + /** + * @var EntityManager The EntityManager used by this QueryBuilder. + */ + private $_em; + + /** + * @var array The array of DQL parts collected. + */ + private $_dqlParts = array( + 'distinct' => false, + 'select' => array(), + 'from' => array(), + 'join' => array(), + 'set' => array(), + 'where' => null, + 'groupBy' => array(), + 'having' => null, + 'orderBy' => array() + ); + + /** + * @var integer The type of query this is. Can be select, update or delete. + */ + private $_type = self::SELECT; + + /** + * @var integer The state of the query object. Can be dirty or clean. + */ + private $_state = self::STATE_CLEAN; + + /** + * @var string The complete DQL string for this query. + */ + private $_dql; + + /** + * @var array The query parameters. + */ + private $_params = array(); + + /** + * @var array The parameter type map of this query. + */ + private $_paramTypes = array(); + + /** + * @var integer The index of the first result to retrieve. + */ + private $_firstResult = null; + + /** + * @var integer The maximum number of results to retrieve. + */ + private $_maxResults = null; + + /** + * Initializes a new QueryBuilder that uses the given EntityManager. + * + * @param EntityManager $em The EntityManager to use. + */ + public function __construct(EntityManager $em) + { + $this->_em = $em; + } + + /** + * Gets an ExpressionBuilder used for object-oriented construction of query expressions. + * This producer method is intended for convenient inline usage. Example: + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where($qb->expr()->eq('u.id', 1)); + * + * + * For more complex expression construction, consider storing the expression + * builder object in a local variable. + * + * @return Query\Expr + */ + public function expr() + { + return $this->_em->getExpressionBuilder(); + } + + /** + * Get the type of the currently built query. + * + * @return integer + */ + public function getType() + { + return $this->_type; + } + + /** + * Get the associated EntityManager for this query builder. + * + * @return EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * Get the state of this query builder instance. + * + * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. + */ + public function getState() + { + return $this->_state; + } + + /** + * Get the complete DQL string formed by the current specifications of this QueryBuilder. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * echo $qb->getDql(); // SELECT u FROM User u + * + * + * @return string The DQL query string. + */ + public function getDQL() + { + if ($this->_dql !== null && $this->_state === self::STATE_CLEAN) { + return $this->_dql; + } + + $dql = ''; + + switch ($this->_type) { + case self::DELETE: + $dql = $this->_getDQLForDelete(); + break; + + case self::UPDATE: + $dql = $this->_getDQLForUpdate(); + break; + + case self::SELECT: + default: + $dql = $this->_getDQLForSelect(); + break; + } + + $this->_state = self::STATE_CLEAN; + $this->_dql = $dql; + + return $dql; + } + + /** + * Constructs a Query instance from the current specifications of the builder. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * $q = $qb->getQuery(); + * $results = $q->execute(); + * + * + * @return Query + */ + public function getQuery() + { + return $this->_em->createQuery($this->getDQL()) + ->setParameters($this->_params, $this->_paramTypes) + ->setFirstResult($this->_firstResult) + ->setMaxResults($this->_maxResults); + } + + /** + * Gets the FIRST root alias of the query. This is the first entity alias involved + * in the construction of the query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * + * echo $qb->getRootAlias(); // u + * + * + * @deprecated Please use $qb->getRootAliases() instead. + * @return string $rootAlias + */ + public function getRootAlias() + { + $aliases = $this->getRootAliases(); + return $aliases[0]; + } + + /** + * Gets the root aliases of the query. This is the entity aliases involved + * in the construction of the query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * + * $qb->getRootAliases(); // array('u') + * + * + * @return array $rootAliases + */ + public function getRootAliases() + { + $aliases = array(); + + foreach ($this->_dqlParts['from'] as &$fromClause) { + if (is_string($fromClause)) { + $spacePos = strrpos($fromClause, ' '); + $from = substr($fromClause, 0, $spacePos); + $alias = substr($fromClause, $spacePos + 1); + + $fromClause = new Query\Expr\From($from, $alias); + } + + $aliases[] = $fromClause->getAlias(); + } + + return $aliases; + } + + /** + * Gets the root entities of the query. This is the entity aliases involved + * in the construction of the query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * + * $qb->getRootEntities(); // array('User') + * + * + * @return array $rootEntities + */ + public function getRootEntities() + { + $entities = array(); + + foreach ($this->_dqlParts['from'] as &$fromClause) { + if (is_string($fromClause)) { + $spacePos = strrpos($fromClause, ' '); + $from = substr($fromClause, 0, $spacePos); + $alias = substr($fromClause, $spacePos + 1); + + $fromClause = new Query\Expr\From($from, $alias); + } + + $entities[] = $fromClause->getFrom(); + } + + return $entities; + } + + /** + * Sets a query parameter for the query being constructed. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.id = :user_id') + * ->setParameter('user_id', 1); + * + * + * @param string|integer $key The parameter position or name. + * @param mixed $value The parameter value. + * @param string|null $type PDO::PARAM_* or \Doctrine\DBAL\Types\Type::* constant + * @return QueryBuilder This QueryBuilder instance. + */ + public function setParameter($key, $value, $type = null) + { + $key = trim($key, ':'); + + if ($type === null) { + $type = Query\ParameterTypeInferer::inferType($value); + } + + $this->_paramTypes[$key] = $type; + $this->_params[$key] = $value; + + return $this; + } + + /** + * Sets a collection of query parameters for the query being constructed. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.id = :user_id1 OR u.id = :user_id2') + * ->setParameters(array( + * 'user_id1' => 1, + * 'user_id2' => 2 + )); + * + * + * @param array $params The query parameters to set. + * @return QueryBuilder This QueryBuilder instance. + */ + public function setParameters(array $params, array $types = array()) + { + foreach ($params as $key => $value) { + $this->setParameter($key, $value, isset($types[$key]) ? $types[$key] : null); + } + + return $this; + } + + /** + * Gets all defined query parameters for the query being constructed. + * + * @return array The currently defined query parameters. + */ + public function getParameters() + { + return $this->_params; + } + + /** + * Gets a (previously set) query parameter of the query being constructed. + * + * @param mixed $key The key (index or name) of the bound parameter. + * @return mixed The value of the bound parameter. + */ + public function getParameter($key) + { + return isset($this->_params[$key]) ? $this->_params[$key] : null; + } + + /** + * Sets the position of the first result to retrieve (the "offset"). + * + * @param integer $firstResult The first result to return. + * @return QueryBuilder This QueryBuilder instance. + */ + public function setFirstResult($firstResult) + { + $this->_firstResult = $firstResult; + + return $this; + } + + /** + * Gets the position of the first result the query object was set to retrieve (the "offset"). + * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder. + * + * @return integer The position of the first result. + */ + public function getFirstResult() + { + return $this->_firstResult; + } + + /** + * Sets the maximum number of results to retrieve (the "limit"). + * + * @param integer $maxResults The maximum number of results to retrieve. + * @return QueryBuilder This QueryBuilder instance. + */ + public function setMaxResults($maxResults) + { + $this->_maxResults = $maxResults; + + return $this; + } + + /** + * Gets the maximum number of results the query object was set to retrieve (the "limit"). + * Returns NULL if {@link setMaxResults} was not applied to this query builder. + * + * @return integer Maximum number of results. + */ + public function getMaxResults() + { + return $this->_maxResults; + } + + /** + * Either appends to or replaces a single, generic query part. + * + * The available parts are: 'select', 'from', 'join', 'set', 'where', + * 'groupBy', 'having' and 'orderBy'. + * + * @param string $dqlPartName + * @param string $dqlPart + * @param string $append + * @return QueryBuilder This QueryBuilder instance. + */ + public function add($dqlPartName, $dqlPart, $append = false) + { + $isMultiple = is_array($this->_dqlParts[$dqlPartName]); + + // This is introduced for backwards compatibility reasons. + // TODO: Remove for 3.0 + if ($dqlPartName == 'join') { + $newDqlPart = array(); + foreach ($dqlPart AS $k => $v) { + if (is_numeric($k)) { + $newDqlPart[$this->getRootAlias()] = $v; + } else { + $newDqlPart[$k] = $v; + } + } + $dqlPart = $newDqlPart; + } + + if ($append && $isMultiple) { + if (is_array($dqlPart)) { + $key = key($dqlPart); + + $this->_dqlParts[$dqlPartName][$key][] = $dqlPart[$key]; + } else { + $this->_dqlParts[$dqlPartName][] = $dqlPart; + } + } else { + $this->_dqlParts[$dqlPartName] = ($isMultiple) ? array($dqlPart) : $dqlPart; + } + + $this->_state = self::STATE_DIRTY; + + return $this; + } + + /** + * Specifies an item that is to be returned in the query result. + * Replaces any previously specified selections, if any. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u', 'p') + * ->from('User', 'u') + * ->leftJoin('u.Phonenumbers', 'p'); + * + * + * @param mixed $select The selection expressions. + * @return QueryBuilder This QueryBuilder instance. + */ + public function select($select = null) + { + $this->_type = self::SELECT; + + if (empty($select)) { + return $this; + } + + $selects = is_array($select) ? $select : func_get_args(); + + return $this->add('select', new Expr\Select($selects), false); + } + + /** + * Add a DISTINCT flag to this query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->distinct() + * ->from('User', 'u'); + * + * + * @param bool + * @return QueryBuilder + */ + public function distinct($flag = true) + { + $this->_dqlParts['distinct'] = (bool) $flag; + return $this; + } + + /** + * Adds an item that is to be returned in the query result. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->addSelect('p') + * ->from('User', 'u') + * ->leftJoin('u.Phonenumbers', 'p'); + * + * + * @param mixed $select The selection expression. + * @return QueryBuilder This QueryBuilder instance. + */ + public function addSelect($select = null) + { + $this->_type = self::SELECT; + + if (empty($select)) { + return $this; + } + + $selects = is_array($select) ? $select : func_get_args(); + + return $this->add('select', new Expr\Select($selects), true); + } + + /** + * Turns the query being built into a bulk delete query that ranges over + * a certain entity type. + * + * + * $qb = $em->createQueryBuilder() + * ->delete('User', 'u') + * ->where('u.id = :user_id'); + * ->setParameter('user_id', 1); + * + * + * @param string $delete The class/type whose instances are subject to the deletion. + * @param string $alias The class/type alias used in the constructed query. + * @return QueryBuilder This QueryBuilder instance. + */ + public function delete($delete = null, $alias = null) + { + $this->_type = self::DELETE; + + if ( ! $delete) { + return $this; + } + + return $this->add('from', new Expr\From($delete, $alias)); + } + + /** + * Turns the query being built into a bulk update query that ranges over + * a certain entity type. + * + * + * $qb = $em->createQueryBuilder() + * ->update('User', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $update The class/type whose instances are subject to the update. + * @param string $alias The class/type alias used in the constructed query. + * @return QueryBuilder This QueryBuilder instance. + */ + public function update($update = null, $alias = null) + { + $this->_type = self::UPDATE; + + if ( ! $update) { + return $this; + } + + return $this->add('from', new Expr\From($update, $alias)); + } + + /** + * Create and add a query root corresponding to the entity identified by the given alias, + * forming a cartesian product with any existing query roots. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * + * + * @param string $from The class name. + * @param string $alias The alias of the class. + * @param string $indexBy The index for the from. + * @return QueryBuilder This QueryBuilder instance. + */ + public function from($from, $alias, $indexBy = null) + { + return $this->add('from', new Expr\From($from, $alias, $indexBy), true); + } + + /** + * Creates and adds a join over an entity association to the query. + * + * The entities in the joined association will be fetched as part of the query + * result if the alias used for the joined association is placed in the select + * expressions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->join('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); + * + * + * @param string $join The relationship to join + * @param string $alias The alias of the join + * @param string $conditionType The condition type constant. Either ON or WITH. + * @param string $condition The condition for the join + * @param string $indexBy The index for the join + * @return QueryBuilder This QueryBuilder instance. + */ + public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null) + { + return $this->innerJoin($join, $alias, $conditionType, $condition, $indexBy); + } + + /** + * Creates and adds a join over an entity association to the query. + * + * The entities in the joined association will be fetched as part of the query + * result if the alias used for the joined association is placed in the select + * expressions. + * + * [php] + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->innerJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); + * + * @param string $join The relationship to join + * @param string $alias The alias of the join + * @param string $conditionType The condition type constant. Either ON or WITH. + * @param string $condition The condition for the join + * @param string $indexBy The index for the join + * @return QueryBuilder This QueryBuilder instance. + */ + public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) + { + $rootAlias = substr($join, 0, strpos($join, '.')); + + if ( ! in_array($rootAlias, $this->getRootAliases())) { + $rootAlias = $this->getRootAlias(); + } + + $join = new Expr\Join( + Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition, $indexBy + ); + + return $this->add('join', array($rootAlias => $join), true); + } + + /** + * Creates and adds a left join over an entity association to the query. + * + * The entities in the joined association will be fetched as part of the query + * result if the alias used for the joined association is placed in the select + * expressions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); + * + * + * @param string $join The relationship to join + * @param string $alias The alias of the join + * @param string $conditionType The condition type constant. Either ON or WITH. + * @param string $condition The condition for the join + * @param string $indexBy The index for the join + * @return QueryBuilder This QueryBuilder instance. + */ + public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) + { + $rootAlias = substr($join, 0, strpos($join, '.')); + + if ( ! in_array($rootAlias, $this->getRootAliases())) { + $rootAlias = $this->getRootAlias(); + } + + $join = new Expr\Join( + Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy + ); + + return $this->add('join', array($rootAlias => $join), true); + } + + /** + * Sets a new value for a field in a bulk update query. + * + * + * $qb = $em->createQueryBuilder() + * ->update('User', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $key The key/field to set. + * @param string $value The value, expression, placeholder, etc. + * @return QueryBuilder This QueryBuilder instance. + */ + public function set($key, $value) + { + return $this->add('set', new Expr\Comparison($key, Expr\Comparison::EQ, $value), true); + } + + /** + * Specifies one or more restrictions to the query result. + * Replaces any previously specified restrictions, if any. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.id = ?'); + * + * // You can optionally programatically build and/or expressions + * $qb = $em->createQueryBuilder(); + * + * $or = $qb->expr()->orx(); + * $or->add($qb->expr()->eq('u.id', 1)); + * $or->add($qb->expr()->eq('u.id', 2)); + * + * $qb->update('User', 'u') + * ->set('u.password', md5('password')) + * ->where($or); + * + * + * @param mixed $predicates The restriction predicates. + * @return QueryBuilder This QueryBuilder instance. + */ + public function where($predicates) + { + if ( ! (func_num_args() == 1 && $predicates instanceof Expr\Composite)) { + $predicates = new Expr\Andx(func_get_args()); + } + + return $this->add('where', $predicates); + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * conjunction with any previously specified restrictions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.username LIKE ?') + * ->andWhere('u.is_active = 1'); + * + * + * @param mixed $where The query restrictions. + * @return QueryBuilder This QueryBuilder instance. + * @see where() + */ + public function andWhere($where) + { + $where = $this->getDQLPart('where'); + $args = func_get_args(); + + if ($where instanceof Expr\Andx) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new Expr\Andx($args); + } + + return $this->add('where', $where, true); + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * disjunction with any previously specified restrictions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.id = 1') + * ->orWhere('u.id = 2'); + * + * + * @param mixed $where The WHERE statement + * @return QueryBuilder $qb + * @see where() + */ + public function orWhere($where) + { + $where = $this->getDqlPart('where'); + $args = func_get_args(); + + if ($where instanceof Expr\Orx) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new Expr\Orx($args); + } + + return $this->add('where', $where, true); + } + + /** + * Specifies a grouping over the results of the query. + * Replaces any previously specified groupings, if any. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->groupBy('u.id'); + * + * + * @param string $groupBy The grouping expression. + * @return QueryBuilder This QueryBuilder instance. + */ + public function groupBy($groupBy) + { + return $this->add('groupBy', new Expr\GroupBy(func_get_args())); + } + + + /** + * Adds a grouping expression to the query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->groupBy('u.lastLogin'); + * ->addGroupBy('u.createdAt') + * + * + * @param string $groupBy The grouping expression. + * @return QueryBuilder This QueryBuilder instance. + */ + public function addGroupBy($groupBy) + { + return $this->add('groupBy', new Expr\GroupBy(func_get_args()), true); + } + + /** + * Specifies a restriction over the groups of the query. + * Replaces any previous having restrictions, if any. + * + * @param mixed $having The restriction over the groups. + * @return QueryBuilder This QueryBuilder instance. + */ + public function having($having) + { + if ( ! (func_num_args() == 1 && ($having instanceof Expr\Andx || $having instanceof Expr\Orx))) { + $having = new Expr\Andx(func_get_args()); + } + + return $this->add('having', $having); + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * conjunction with any existing having restrictions. + * + * @param mixed $having The restriction to append. + * @return QueryBuilder This QueryBuilder instance. + */ + public function andHaving($having) + { + $having = $this->getDqlPart('having'); + $args = func_get_args(); + + if ($having instanceof Expr\Andx) { + $having->addMultiple($args); + } else { + array_unshift($args, $having); + $having = new Expr\Andx($args); + } + + return $this->add('having', $having); + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * disjunction with any existing having restrictions. + * + * @param mixed $having The restriction to add. + * @return QueryBuilder This QueryBuilder instance. + */ + public function orHaving($having) + { + $having = $this->getDqlPart('having'); + $args = func_get_args(); + + if ($having instanceof Expr\Orx) { + $having->addMultiple($args); + } else { + array_unshift($args, $having); + $having = new Expr\Orx($args); + } + + return $this->add('having', $having); + } + + /** + * Specifies an ordering for the query results. + * Replaces any previously specified orderings, if any. + * + * @param string $sort The ordering expression. + * @param string $order The ordering direction. + * @return QueryBuilder This QueryBuilder instance. + */ + public function orderBy($sort, $order = null) + { + return $this->add('orderBy', $sort instanceof Expr\OrderBy ? $sort + : new Expr\OrderBy($sort, $order)); + } + + /** + * Adds an ordering to the query results. + * + * @param string $sort The ordering expression. + * @param string $order The ordering direction. + * @return QueryBuilder This QueryBuilder instance. + */ + public function addOrderBy($sort, $order = null) + { + return $this->add('orderBy', new Expr\OrderBy($sort, $order), true); + } + + /** + * Get a query part by its name. + * + * @param string $queryPartName + * @return mixed $queryPart + * @todo Rename: getQueryPart (or remove?) + */ + public function getDQLPart($queryPartName) + { + return $this->_dqlParts[$queryPartName]; + } + + /** + * Get all query parts. + * + * @return array $dqlParts + * @todo Rename: getQueryParts (or remove?) + */ + public function getDQLParts() + { + return $this->_dqlParts; + } + + private function _getDQLForDelete() + { + return 'DELETE' + . $this->_getReducedDQLQueryPart('from', array('pre' => ' ', 'separator' => ', ')) + . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE ')) + . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); + } + + private function _getDQLForUpdate() + { + return 'UPDATE' + . $this->_getReducedDQLQueryPart('from', array('pre' => ' ', 'separator' => ', ')) + . $this->_getReducedDQLQueryPart('set', array('pre' => ' SET ', 'separator' => ', ')) + . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE ')) + . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); + } + + private function _getDQLForSelect() + { + $dql = 'SELECT' + . ($this->_dqlParts['distinct']===true ? ' DISTINCT' : '') + . $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', ')); + + $fromParts = $this->getDQLPart('from'); + $joinParts = $this->getDQLPart('join'); + $fromClauses = array(); + + // Loop through all FROM clauses + if ( ! empty($fromParts)) { + $dql .= ' FROM '; + + foreach ($fromParts as $from) { + $fromClause = (string) $from; + + if ($from instanceof Expr\From && isset($joinParts[$from->getAlias()])) { + foreach ($joinParts[$from->getAlias()] as $join) { + $fromClause .= ' ' . ((string) $join); + } + } + + $fromClauses[] = $fromClause; + } + } + + $dql .= implode(', ', $fromClauses) + . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE ')) + . $this->_getReducedDQLQueryPart('groupBy', array('pre' => ' GROUP BY ', 'separator' => ', ')) + . $this->_getReducedDQLQueryPart('having', array('pre' => ' HAVING ')) + . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); + + return $dql; + } + + private function _getReducedDQLQueryPart($queryPartName, $options = array()) + { + $queryPart = $this->getDQLPart($queryPartName); + + if (empty($queryPart)) { + return (isset($options['empty']) ? $options['empty'] : ''); + } + + return (isset($options['pre']) ? $options['pre'] : '') + . (is_array($queryPart) ? implode($options['separator'], $queryPart) : $queryPart) + . (isset($options['post']) ? $options['post'] : ''); + } + + /** + * Reset DQL parts + * + * @param array $parts + * @return QueryBuilder + */ + public function resetDQLParts($parts = null) + { + if (is_null($parts)) { + $parts = array_keys($this->_dqlParts); + } + foreach ($parts as $part) { + $this->resetDQLPart($part); + } + return $this; + } + + /** + * Reset single DQL part + * + * @param string $part + * @return QueryBuilder; + */ + public function resetDQLPart($part) + { + if (is_array($this->_dqlParts[$part])) { + $this->_dqlParts[$part] = array(); + } else { + $this->_dqlParts[$part] = null; + } + $this->_state = self::STATE_DIRTY; + return $this; + } + + /** + * Gets a string representation of this QueryBuilder which corresponds to + * the final DQL query being constructed. + * + * @return string The string representation of this QueryBuilder. + */ + public function __toString() + { + return $this->getDQL(); + } + + /** + * Deep clone of all expression objects in the DQL parts. + * + * @return void + */ + public function __clone() + { + foreach ($this->_dqlParts AS $part => $elements) { + if (is_array($this->_dqlParts[$part])) { + foreach ($this->_dqlParts[$part] AS $idx => $element) { + if (is_object($element)) { + $this->_dqlParts[$part][$idx] = clone $element; + } + } + } else if (\is_object($elements)) { + $this->_dqlParts[$part] = clone $elements; + } + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/README.markdown b/vendor/doctrine/orm/lib/Doctrine/ORM/README.markdown new file mode 100644 index 0000000..e69de29 diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php new file mode 100644 index 0000000..afff8db --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\Common\Cache; + +/** + * Command to clear the metadata cache of the various cache drivers. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class MetadataCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:metadata') + ->setDescription('Clear all metadata cache of the various cache drivers.') + ->setDefinition(array( + new InputOption( + 'flush', null, InputOption::VALUE_NONE, + 'If defined, cache entries will be flushed instead of deleted/invalidated.' + ) + )); + + $fullName = $this->getName(); + $this->setHelp(<<$fullName command is meant to clear the metadata cache of associated Entity Manager. +It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider +instance completely. + +The execution type differ on how you execute the command. +If you want to invalidate the entries (and not delete from cache instance), this command would do the work: + +$fullName + +Alternatively, if you want to flush the cache provider using this command: + +$fullName --flush + +Finally, be aware that if --flush option is passed, not all cache providers are able to flush entries, +because of a limitation of its execution nature. +EOT + ); + } + + /** + * @see Console\Command\Command + */ + protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $cacheDriver = $em->getConfiguration()->getMetadataCacheImpl(); + + if ( ! $cacheDriver) { + throw new \InvalidArgumentException('No Metadata cache driver is configured on given EntityManager.'); + } + + if ($cacheDriver instanceof Cache\ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + $output->write('Clearing ALL Metadata cache entries' . PHP_EOL); + + $result = $cacheDriver->deleteAll(); + $message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.'; + + if (true === $input->getOption('flush')) { + $result = $cacheDriver->flushAll(); + $message = ($result) ? 'Successfully flushed cache entries.' : $message; + } + + $output->write($message . PHP_EOL); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php new file mode 100644 index 0000000..6ad75cd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\Common\Cache; + +/** + * Command to clear the query cache of the various cache drivers. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class QueryCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:query') + ->setDescription('Clear all query cache of the various cache drivers.') + ->setDefinition(array( + new InputOption( + 'flush', null, InputOption::VALUE_NONE, + 'If defined, cache entries will be flushed instead of deleted/invalidated.' + ) + )); + + $fullName = $this->getName(); + $this->setHelp(<<$fullName command is meant to clear the query cache of associated Entity Manager. +It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider +instance completely. + +The execution type differ on how you execute the command. +If you want to invalidate the entries (and not delete from cache instance), this command would do the work: + +$fullName + +Alternatively, if you want to flush the cache provider using this command: + +$fullName --flush + +Finally, be aware that if --flush option is passed, not all cache providers are able to flush entries, +because of a limitation of its execution nature. +EOT + ); + } + + /** + * @see Console\Command\Command + */ + protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $cacheDriver = $em->getConfiguration()->getQueryCacheImpl(); + + if ( ! $cacheDriver) { + throw new \InvalidArgumentException('No Query cache driver is configured on given EntityManager.'); + } + + if ($cacheDriver instanceof Cache\ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + $output->write('Clearing ALL Query cache entries' . PHP_EOL); + + $result = $cacheDriver->deleteAll(); + $message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.'; + + if (true === $input->getOption('flush')) { + $result = $cacheDriver->flushAll(); + $message = ($result) ? 'Successfully flushed cache entries.' : $message; + } + + $output->write($message . PHP_EOL); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php new file mode 100644 index 0000000..5bb000c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\Common\Cache; + +/** + * Command to clear the result cache of the various cache drivers. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ResultCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:result') + ->setDescription('Clear all result cache of the various cache drivers.') + ->setDefinition(array( + new InputOption( + 'flush', null, InputOption::VALUE_NONE, + 'If defined, cache entries will be flushed instead of deleted/invalidated.' + ) + )); + + $fullName = $this->getName(); + $this->setHelp(<<$fullName command is meant to clear the result cache of associated Entity Manager. +It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider +instance completely. + +The execution type differ on how you execute the command. +If you want to invalidate the entries (and not delete from cache instance), this command would do the work: + +$fullName + +Alternatively, if you want to flush the cache provider using this command: + +$fullName --flush + +Finally, be aware that if --flush option is passed, not all cache providers are able to flush entries, +because of a limitation of its execution nature. +EOT + ); + } + + /** + * @see Console\Command\Command + */ + protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $cacheDriver = $em->getConfiguration()->getResultCacheImpl(); + + if ( ! $cacheDriver) { + throw new \InvalidArgumentException('No Result cache driver is configured on given EntityManager.'); + } + + if ($cacheDriver instanceof Cache\ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + $output->write('Clearing ALL Result cache entries' . PHP_EOL); + + $result = $cacheDriver->deleteAll(); + $message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.'; + + if (true === $input->getOption('flush')) { + $result = $cacheDriver->flushAll(); + $message = ($result) ? 'Successfully flushed cache entries.' : $message; + } + + $output->write($message . PHP_EOL); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php new file mode 100644 index 0000000..c78920b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php @@ -0,0 +1,223 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\ORM\Tools\Export\ClassMetadataExporter, + Doctrine\ORM\Tools\ConvertDoctrine1Schema, + Doctrine\ORM\Tools\EntityGenerator; + +/** + * Command to convert a Doctrine 1 schema to a Doctrine 2 mapping file. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConvertDoctrine1SchemaCommand extends Console\Command\Command +{ + /** + * @var EntityGenerator + */ + private $entityGenerator = null; + + /** + * @var ClassMetadataExporter + */ + private $metadataExporter = null; + + /** + * @return EntityGenerator + */ + public function getEntityGenerator() + { + if ($this->entityGenerator == null) { + $this->entityGenerator = new EntityGenerator(); + } + + return $this->entityGenerator; + } + + /** + * @param EntityGenerator $entityGenerator + */ + public function setEntityGenerator(EntityGenerator $entityGenerator) + { + $this->entityGenerator = $entityGenerator; + } + + /** + * @return ClassMetadataExporter + */ + public function getMetadataExporter() + { + if ($this->metadataExporter == null) { + $this->metadataExporter = new ClassMetadataExporter(); + } + + return $this->metadataExporter; + } + + /** + * @param ClassMetadataExporter $metadataExporter + */ + public function setMetadataExporter(ClassMetadataExporter $metadataExporter) + { + $this->metadataExporter = $metadataExporter; + } + + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('orm:convert-d1-schema') + ->setDescription('Converts Doctrine 1.X schema into a Doctrine 2.X schema.') + ->setDefinition(array( + new InputArgument( + 'from-path', InputArgument::REQUIRED, 'The path of Doctrine 1.X schema information.' + ), + new InputArgument( + 'to-type', InputArgument::REQUIRED, 'The destination Doctrine 2.X mapping type.' + ), + new InputArgument( + 'dest-path', InputArgument::REQUIRED, + 'The path to generate your Doctrine 2.X mapping information.' + ), + new InputOption( + 'from', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'Optional paths of Doctrine 1.X schema information.', + array() + ), + new InputOption( + 'extend', null, InputOption::VALUE_OPTIONAL, + 'Defines a base class to be extended by generated entity classes.' + ), + new InputOption( + 'num-spaces', null, InputOption::VALUE_OPTIONAL, + 'Defines the number of indentation spaces', 4 + ) + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + // Process source directories + $fromPaths = array_merge(array($input->getArgument('from-path')), $input->getOption('from')); + + // Process destination directory + $destPath = realpath($input->getArgument('dest-path')); + + $toType = $input->getArgument('to-type'); + $extend = $input->getOption('extend'); + $numSpaces = $input->getOption('num-spaces'); + + $this->convertDoctrine1Schema($em, $fromPaths, $destPath, $toType, $numSpaces, $extend, $output); + } + + /** + * @param \Doctrine\ORM\EntityManager $em + * @param array $fromPaths + * @param string $destPath + * @param string $toType + * @param int $numSpaces + * @param string|null $extend + * @param Console\Output\OutputInterface $output + */ + public function convertDoctrine1Schema($em, $fromPaths, $destPath, $toType, $numSpaces, $extend, $output) + { + foreach ($fromPaths as &$dirName) { + $dirName = realpath($dirName); + + if ( ! file_exists($dirName)) { + throw new \InvalidArgumentException( + sprintf("Doctrine 1.X schema directory '%s' does not exist.", $dirName) + ); + } else if ( ! is_readable($dirName)) { + throw new \InvalidArgumentException( + sprintf("Doctrine 1.X schema directory '%s' does not have read permissions.", $dirName) + ); + } + } + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Doctrine 2.X mapping destination directory '%s' does not exist.", $destPath) + ); + } else if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Doctrine 2.X mapping destination directory '%s' does not have write permissions.", $destPath) + ); + } + + $cme = $this->getMetadataExporter(); + $exporter = $cme->getExporter($toType, $destPath); + + if (strtolower($toType) === 'annotation') { + $entityGenerator = $this->getEntityGenerator(); + $exporter->setEntityGenerator($entityGenerator); + + $entityGenerator->setNumSpaces($numSpaces); + + if ($extend !== null) { + $entityGenerator->setClassToExtend($extend); + } + } + + $converter = new ConvertDoctrine1Schema($fromPaths); + $metadata = $converter->getMetadata(); + + if ($metadata) { + $output->write(PHP_EOL); + + foreach ($metadata as $class) { + $output->write(sprintf('Processing entity "%s"', $class->name) . PHP_EOL); + } + + $exporter->setMetadata($metadata); + $exporter->export(); + + $output->write(PHP_EOL . sprintf( + 'Converting Doctrine 1.X schema to "%s" mapping type in "%s"', $toType, $destPath + )); + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php new file mode 100644 index 0000000..03af43a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -0,0 +1,186 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\ORM\Tools\Console\MetadataFilter, + Doctrine\ORM\Tools\Export\ClassMetadataExporter, + Doctrine\ORM\Tools\EntityGenerator, + Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; + +/** + * Command to convert your mapping information between the various formats. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConvertMappingCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('orm:convert-mapping') + ->setDescription('Convert mapping information between supported formats.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match entities that should be processed.' + ), + new InputArgument( + 'to-type', InputArgument::REQUIRED, 'The mapping type to be converted.' + ), + new InputArgument( + 'dest-path', InputArgument::REQUIRED, + 'The path to generate your entities classes.' + ), + new InputOption( + 'force', null, InputOption::VALUE_NONE, + 'Force to overwrite existing mapping files.' + ), + new InputOption( + 'from-database', null, null, 'Whether or not to convert mapping information from existing database.' + ), + new InputOption( + 'extend', null, InputOption::VALUE_OPTIONAL, + 'Defines a base class to be extended by generated entity classes.' + ), + new InputOption( + 'num-spaces', null, InputOption::VALUE_OPTIONAL, + 'Defines the number of indentation spaces', 4 + ), + new InputOption( + 'namespace', null, InputOption::VALUE_OPTIONAL, + 'Defines a namespace for the generated entity classes, if converted from database.' + ), + )) + ->setHelp(<<one-time command. It should not be necessary for +you to call this method multiple times, escpecially when using the --from-database +flag. + +Converting an existing databsae schema into mapping files only solves about 70-80% +of the necessary mapping information. Additionally the detection from an existing +database cannot detect inverse associations, inheritance types, +entities with foreign keys as primary keys and many of the +semantical operations on associations such as cascade. + +Hint: There is no need to convert YAML or XML mapping files to annotations +every time you make changes. All mapping drivers are first class citizens +in Doctrine 2 and can be used as runtime mapping for the ORM. +EOT + ); + } + + /** + * @see Console\Command\Command + */ + protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + + if ($input->getOption('from-database') === true) { + $databaseDriver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver( + $em->getConnection()->getSchemaManager() + ); + + $em->getConfiguration()->setMetadataDriverImpl( + $databaseDriver + ); + + if (($namespace = $input->getOption('namespace')) !== null) { + $databaseDriver->setNamespace($namespace); + } + } + + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + $metadata = $cmf->getAllMetadata(); + $metadata = MetadataFilter::filter($metadata, $input->getOption('filter')); + + // Process destination directory + if ( ! is_dir($destPath = $input->getArgument('dest-path'))) { + mkdir($destPath, 0777, true); + } + $destPath = realpath($destPath); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Mapping destination directory '%s' does not exist.", $input->getArgument('dest-path')) + ); + } else if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Mapping destination directory '%s' does not have write permissions.", $destPath) + ); + } + + $toType = strtolower($input->getArgument('to-type')); + + $exporter = $this->getExporter($toType, $destPath); + $exporter->setOverwriteExistingFiles( ($input->getOption('force') !== false) ); + + if ($toType == 'annotation') { + $entityGenerator = new EntityGenerator(); + $exporter->setEntityGenerator($entityGenerator); + + $entityGenerator->setNumSpaces($input->getOption('num-spaces')); + + if (($extend = $input->getOption('extend')) !== null) { + $entityGenerator->setClassToExtend($extend); + } + } + + if (count($metadata)) { + foreach ($metadata as $class) { + $output->write(sprintf('Processing entity "%s"', $class->name) . PHP_EOL); + } + + $exporter->setMetadata($metadata); + $exporter->export(); + + $output->write(PHP_EOL . sprintf( + 'Exporting "%s" mapping information to "%s"' . PHP_EOL, $toType, $destPath + )); + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + } + + protected function getExporter($toType, $destPath) + { + $cme = new ClassMetadataExporter(); + + return $cme->getExporter($toType, $destPath); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php new file mode 100644 index 0000000..d29bfce --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php @@ -0,0 +1,85 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console; + +/** + * Command to ensure that Doctrine is properly configured for a production environment. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EnsureProductionSettingsCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('orm:ensure-production-settings') + ->setDescription('Verify that Doctrine is properly configured for a production environment.') + ->setDefinition(array( + new InputOption( + 'complete', null, InputOption::VALUE_NONE, + 'Flag to also inspect database connection existance.' + ) + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + $error = false; + try { + $em->getConfiguration()->ensureProductionSettings(); + + if ($input->getOption('complete') !== null) { + $em->getConnection()->connect(); + } + } catch (\Exception $e) { + $error = true; + $output->writeln('' . $e->getMessage() . ''); + } + + if ($error === false) { + $output->write('Environment is correctly configured for production.' . PHP_EOL); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php new file mode 100644 index 0000000..f00e6d2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php @@ -0,0 +1,163 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\ORM\Tools\Console\MetadataFilter, + Doctrine\ORM\Tools\EntityGenerator, + Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; + +/** + * Command to generate entity classes and method stubs from your mapping information. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateEntitiesCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('orm:generate-entities') + ->setDescription('Generate entity classes and method stubs from your mapping information.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match entities that should be processed.' + ), + new InputArgument( + 'dest-path', InputArgument::REQUIRED, 'The path to generate your entity classes.' + ), + new InputOption( + 'generate-annotations', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should generate annotation metadata on entities.', false + ), + new InputOption( + 'generate-methods', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should generate stub methods on entities.', true + ), + new InputOption( + 'regenerate-entities', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should regenerate entity if it exists.', false + ), + new InputOption( + 'update-entities', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should only update entity if it exists.', true + ), + new InputOption( + 'extend', null, InputOption::VALUE_OPTIONAL, + 'Defines a base class to be extended by generated entity classes.' + ), + new InputOption( + 'num-spaces', null, InputOption::VALUE_OPTIONAL, + 'Defines the number of indentation spaces', 4 + ) + )) + ->setHelp(<<--update-entities or --regenerate-entities flags your exisiting +code gets overwritten. The EntityGenerator will only append new code to your +file and will not delete the old code. However this approach may still be prone +to error and we suggest you use code repositories such as GIT or SVN to make +backups of your code. + +It makes sense to generate the entity code if you are using entities as Data +Access Objects only and dont put much additional logic on them. If you are +however putting much more logic on the entities you should refrain from using +the entity-generator and code your entities manually. + +Important: Even if you specified Inheritance options in your +XML or YAML Mapping files the generator cannot generate the base and +child classes for you correctly, because it doesn't know which +class is supposed to extend which. You have to adjust the entity +code manually for inheritance to work! +EOT + ); + } + + /** + * @see Console\Command\Command + */ + protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + $metadatas = $cmf->getAllMetadata(); + $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); + + // Process destination directory + $destPath = realpath($input->getArgument('dest-path')); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Entities destination directory '%s' does not exist.", $input->getArgument('dest-path')) + ); + } else if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Entities destination directory '%s' does not have write permissions.", $destPath) + ); + } + + if (count($metadatas)) { + // Create EntityGenerator + $entityGenerator = new EntityGenerator(); + + $entityGenerator->setGenerateAnnotations($input->getOption('generate-annotations')); + $entityGenerator->setGenerateStubMethods($input->getOption('generate-methods')); + $entityGenerator->setRegenerateEntityIfExists($input->getOption('regenerate-entities')); + $entityGenerator->setUpdateEntityIfExists($input->getOption('update-entities')); + $entityGenerator->setNumSpaces($input->getOption('num-spaces')); + + if (($extend = $input->getOption('extend')) !== null) { + $entityGenerator->setClassToExtend($extend); + } + + foreach ($metadatas as $metadata) { + $output->write( + sprintf('Processing entity "%s"', $metadata->name) . PHP_EOL + ); + } + + // Generating Entities + $entityGenerator->generate($metadatas, $destPath); + + // Outputting information message + $output->write(PHP_EOL . sprintf('Entity classes generated to "%s"', $destPath) . PHP_EOL); + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php new file mode 100644 index 0000000..7028d99 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php @@ -0,0 +1,115 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\ORM\Tools\Console\MetadataFilter; + +/** + * Command to (re)generate the proxy classes used by doctrine. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateProxiesCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('orm:generate-proxies') + ->setDescription('Generates proxy classes for entity classes.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match entities that should be processed.' + ), + new InputArgument( + 'dest-path', InputArgument::OPTIONAL, + 'The path to generate your proxy classes. If none is provided, it will attempt to grab from configuration.' + ), + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + $metadatas = $em->getMetadataFactory()->getAllMetadata(); + $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); + + // Process destination directory + if (($destPath = $input->getArgument('dest-path')) === null) { + $destPath = $em->getConfiguration()->getProxyDir(); + } + + if ( ! is_dir($destPath)) { + mkdir($destPath, 0777, true); + } + + $destPath = realpath($destPath); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Proxies destination directory '%s' does not exist.", $em->getConfiguration()->getProxyDir()) + ); + } else if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Proxies destination directory '%s' does not have write permissions.", $destPath) + ); + } + + if ( count($metadatas)) { + foreach ($metadatas as $metadata) { + $output->write( + sprintf('Processing entity "%s"', $metadata->name) . PHP_EOL + ); + } + + // Generating Proxies + $em->getProxyFactory()->generateProxyClasses($metadatas, $destPath); + + // Outputting information message + $output->write(PHP_EOL . sprintf('Proxy classes generated to "%s"', $destPath) . PHP_EOL); + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php new file mode 100644 index 0000000..bb6fc9d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php @@ -0,0 +1,116 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console, + Doctrine\ORM\Tools\Console\MetadataFilter, + Doctrine\ORM\Tools\EntityRepositoryGenerator; + +/** + * Command to generate repository classes for mapping information. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateRepositoriesCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('orm:generate-repositories') + ->setDescription('Generate repository classes from your mapping information.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match entities that should be processed.' + ), + new InputArgument( + 'dest-path', InputArgument::REQUIRED, 'The path to generate your repository classes.' + ) + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + $metadatas = $em->getMetadataFactory()->getAllMetadata(); + $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); + + // Process destination directory + $destPath = realpath($input->getArgument('dest-path')); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Entities destination directory '%s' does not exist.", $input->getArgument('dest-path')) + ); + } else if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Entities destination directory '%s' does not have write permissions.", $destPath) + ); + } + + if (count($metadatas)) { + $numRepositories = 0; + $generator = new EntityRepositoryGenerator(); + + foreach ($metadatas as $metadata) { + if ($metadata->customRepositoryClassName) { + $output->write( + sprintf('Processing repository "%s"', $metadata->customRepositoryClassName) . PHP_EOL + ); + + $generator->writeEntityRepositoryClass($metadata->customRepositoryClassName, $destPath); + + $numRepositories++; + } + } + + if ($numRepositories) { + // Outputting information message + $output->write(PHP_EOL . sprintf('Repository classes generated to "%s"', $destPath) . PHP_EOL); + } else { + $output->write('No Repository classes were found to be processed.' . PHP_EOL); + } + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php new file mode 100644 index 0000000..77458cf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Doctrine\ORM\Mapping\MappingException; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Show information about mapped entities + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + */ +class InfoCommand extends Command +{ + protected function configure() + { + $this + ->setName('orm:info') + ->setDescription('Show basic information about all mapped entities') + ->setHelp(<<doctrine:mapping:info shows basic information about which +entities exist and possibly if their mapping information contains errors or +not. +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + /* @var $entityManager \Doctrine\ORM\EntityManager */ + $entityManager = $this->getHelper('em')->getEntityManager(); + + $entityClassNames = $entityManager->getConfiguration() + ->getMetadataDriverImpl() + ->getAllClassNames(); + + if (!$entityClassNames) { + throw new \Exception( + 'You do not have any mapped Doctrine ORM entities according to the current configuration. '. + 'If you have entities or mapping files you should check your mapping configuration for errors.' + ); + } + + $output->writeln(sprintf("Found %d mapped entities:", count($entityClassNames))); + + foreach ($entityClassNames as $entityClassName) { + try { + $cm = $entityManager->getClassMetadata($entityClassName); + $output->writeln(sprintf("[OK] %s", $entityClassName)); + } catch (MappingException $e) { + $output->writeln("[FAIL] ".$entityClassName); + $output->writeln(sprintf("%s", $e->getMessage())); + $output->writeln(''); + } + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php new file mode 100644 index 0000000..c794cb9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console; + +/** + * Command to execute DQL queries in a given EntityManager. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class RunDqlCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('orm:run-dql') + ->setDescription('Executes arbitrary DQL directly from the command line.') + ->setDefinition(array( + new InputArgument('dql', InputArgument::REQUIRED, 'The DQL to execute.'), + new InputOption( + 'hydrate', null, InputOption::VALUE_REQUIRED, + 'Hydration mode of result set. Should be either: object, array, scalar or single-scalar.', + 'object' + ), + new InputOption( + 'first-result', null, InputOption::VALUE_REQUIRED, + 'The first result in the result set.' + ), + new InputOption( + 'max-result', null, InputOption::VALUE_REQUIRED, + 'The maximum number of results in the result set.' + ), + new InputOption( + 'depth', null, InputOption::VALUE_REQUIRED, + 'Dumping depth of Entity graph.', 7 + ) + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + if (($dql = $input->getArgument('dql')) === null) { + throw new \RuntimeException("Argument 'DQL' is required in order to execute this command correctly."); + } + + $depth = $input->getOption('depth'); + + if ( ! is_numeric($depth)) { + throw new \LogicException("Option 'depth' must contains an integer value"); + } + + $hydrationModeName = $input->getOption('hydrate'); + $hydrationMode = 'Doctrine\ORM\Query::HYDRATE_' . strtoupper(str_replace('-', '_', $hydrationModeName)); + + if ( ! defined($hydrationMode)) { + throw new \RuntimeException( + "Hydration mode '$hydrationModeName' does not exist. It should be either: object. array, scalar or single-scalar." + ); + } + + $query = $em->createQuery($dql); + + if (($firstResult = $input->getOption('first-result')) !== null) { + if ( ! is_numeric($firstResult)) { + throw new \LogicException("Option 'first-result' must contains an integer value"); + } + + $query->setFirstResult((int) $firstResult); + } + + if (($maxResult = $input->getOption('max-result')) !== null) { + if ( ! is_numeric($maxResult)) { + throw new \LogicException("Option 'max-result' must contains an integer value"); + } + + $query->setMaxResults((int) $maxResult); + } + + $resultSet = $query->execute(array(), constant($hydrationMode)); + + \Doctrine\Common\Util\Debug::dump($resultSet, $input->getOption('depth')); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php new file mode 100644 index 0000000..cd53948 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php @@ -0,0 +1,64 @@ +. +*/ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console\Input\InputInterface, + Symfony\Component\Console\Output\OutputInterface, + Symfony\Component\Console\Command\Command, + Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper, + Doctrine\ORM\Tools\SchemaTool, + Doctrine\ORM\Mapping\Driver\AbstractFileDriver; + +abstract class AbstractCommand extends Command +{ + /** + * @param InputInterface $input + * @param OutputInterface $output + * @param SchemaTool $schemaTool + * @param array $metadatas + */ + abstract protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas); + + /** + * @see Console\Command\Command + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $emHelper = $this->getHelper('em'); + + /* @var $em \Doctrine\ORM\EntityManager */ + $em = $emHelper->getEntityManager(); + + $metadatas = $em->getMetadataFactory()->getAllMetadata(); + + if ( ! empty($metadatas)) { + // Create SchemaTool + $tool = new \Doctrine\ORM\Tools\SchemaTool($em); + + $this->executeSchemaCommand($input, $output, $tool, $metadatas); + } else { + $output->write('No Metadata Classes to process.' . PHP_EOL); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php new file mode 100644 index 0000000..d0b8818 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php @@ -0,0 +1,79 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console\Input\InputInterface, + Symfony\Component\Console\Output\OutputInterface, + Doctrine\ORM\Tools\SchemaTool; + +/** + * Command to create the database schema for a set of classes based on their mappings. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class CreateCommand extends AbstractCommand +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('orm:schema-tool:create') + ->setDescription( + 'Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output.' + ) + ->setDefinition(array( + new InputOption( + 'dump-sql', null, InputOption::VALUE_NONE, + 'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.' + ) + )) + ->setHelp(<<write('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL . PHP_EOL); + + if ($input->getOption('dump-sql') === true) { + $sqls = $schemaTool->getCreateSchemaSql($metadatas); + $output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL); + } else { + $output->write('Creating database schema...' . PHP_EOL); + $schemaTool->createSchema($metadatas); + $output->write('Database schema created successfully!' . PHP_EOL); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php new file mode 100644 index 0000000..57243f2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -0,0 +1,111 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console\Input\InputInterface, + Symfony\Component\Console\Output\OutputInterface, + Doctrine\ORM\Tools\SchemaTool; + +/** + * Command to drop the database schema for a set of classes based on their mappings. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DropCommand extends AbstractCommand +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('orm:schema-tool:drop') + ->setDescription( + 'Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output.' + ) + ->setDefinition(array( + new InputOption( + 'dump-sql', null, InputOption::VALUE_NONE, + 'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.' + ), + new InputOption( + 'force', null, InputOption::VALUE_NONE, + "Don't ask for the deletion of the database, but force the operation to run." + ), + new InputOption( + 'full-database', null, InputOption::VALUE_NONE, + 'Instead of using the Class Metadata to detect the database table schema, drop ALL assets that the database contains.' + ), + )) + ->setHelp(<<getOption('full-database')); + + if ($input->getOption('dump-sql') === true) { + if ($isFullDatabaseDrop) { + $sqls = $schemaTool->getDropDatabaseSQL(); + } else { + $sqls = $schemaTool->getDropSchemaSQL($metadatas); + } + $output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL); + } else if ($input->getOption('force') === true) { + $output->write('Dropping database schema...' . PHP_EOL); + if ($isFullDatabaseDrop) { + $schemaTool->dropDatabase(); + } else { + $schemaTool->dropSchema($metadatas); + } + $output->write('Database schema dropped successfully!' . PHP_EOL); + } else { + $output->write('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL . PHP_EOL); + + if ($isFullDatabaseDrop) { + $sqls = $schemaTool->getDropDatabaseSQL(); + } else { + $sqls = $schemaTool->getDropSchemaSQL($metadatas); + } + + if (count($sqls)) { + $output->write('Schema-Tool would execute ' . count($sqls) . ' queries to drop the database.' . PHP_EOL); + $output->write('Please run the operation with --force to execute these queries or use --dump-sql to see them.' . PHP_EOL); + } else { + $output->write('Nothing to drop. The database is empty!' . PHP_EOL); + } + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php new file mode 100644 index 0000000..2b0fc0d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -0,0 +1,135 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console\Input\InputInterface, + Symfony\Component\Console\Output\OutputInterface, + Doctrine\ORM\Tools\SchemaTool; + +/** + * Command to generate the SQL needed to update the database schema to match + * the current mapping information. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Ryan Weaver + */ +class UpdateCommand extends AbstractCommand +{ + protected $name = 'orm:schema-tool:update'; + + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName($this->name) + ->setDescription( + 'Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata.' + ) + ->setDefinition(array( + new InputOption( + 'complete', null, InputOption::VALUE_NONE, + 'If defined, all assets of the database which are not relevant to the current metadata will be dropped.' + ), + + new InputOption( + 'dump-sql', null, InputOption::VALUE_NONE, + 'Dumps the generated SQL statements to the screen (does not execute them).' + ), + new InputOption( + 'force', null, InputOption::VALUE_NONE, + 'Causes the generated SQL statements to be physically executed against your database.' + ), + )); + + $fullName = $this->getName(); + $this->setHelp(<<$fullName command generates the SQL needed to +synchronize the database schema with the current mapping metadata of the +default entity manager. + +For example, if you add metadata for a new column to an entity, this command +would generate and output the SQL needed to add the new column to the database: + +$fullName --dump-sql + +Alternatively, you can execute the generated queries: + +$fullName --force + +Finally, be aware that if the --complete option is passed, this +task will drop all database assets (e.g. tables, etc) that are *not* described +by the current metadata. In other words, without this option, this task leaves +untouched any "extra" tables that exist in the database, but which aren't +described by any metadata. +EOT + ); + } + + protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas) + { + // Defining if update is complete or not (--complete not defined means $saveMode = true) + $saveMode = ($input->getOption('complete') !== true); + + $sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode); + if (0 == count($sqls)) { + $output->writeln('Nothing to update - your database is already in sync with the current entity metadata.'); + + return; + } + + $dumpSql = (true === $input->getOption('dump-sql')); + $force = (true === $input->getOption('force')); + if ($dumpSql && $force) { + throw new \InvalidArgumentException('You can pass either the --dump-sql or the --force option (but not both simultaneously).'); + } + + if ($dumpSql) { + $output->writeln(implode(';' . PHP_EOL, $sqls)); + } else if ($force) { + $output->writeln('Updating database schema...'); + $schemaTool->updateSchema($metadatas, $saveMode); + $output->writeln(sprintf('Database schema updated successfully! "%s" queries were executed', count($sqls))); + } else { + $output->writeln('ATTENTION: This operation should not be executed in a production environment.'); + $output->writeln(' Use the incremental update to detect changes during development and use'); + $output->writeln(' the SQL DDL provided to manually update your database in production.'); + $output->writeln(''); + + $output->writeln(sprintf('The Schema-Tool would execute "%s" queries to update the database.', count($sqls))); + $output->writeln('Please run the operation by passing one of the following options:'); + + $output->writeln(sprintf(' %s --force to execute the command', $this->getName())); + $output->writeln(sprintf(' %s --dump-sql to dump the SQL statements to the screen', $this->getName())); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php new file mode 100644 index 0000000..9381485 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php @@ -0,0 +1,89 @@ +. +*/ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument, + Symfony\Component\Console\Input\InputOption, + Symfony\Component\Console; + +/** + * Validate that the current mapping is valid + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ValidateSchemaCommand extends Console\Command\Command +{ + /** + * @see Console\Command\Command + */ + protected function configure() + { + $this + ->setName('orm:validate-schema') + ->setDescription('Validate the mapping files.') + ->setHelp(<<getHelper('em')->getEntityManager(); + + $validator = new \Doctrine\ORM\Tools\SchemaValidator($em); + $errors = $validator->validateMapping(); + + $exit = 0; + if ($errors) { + foreach ($errors AS $className => $errorMessages) { + $output->write("[Mapping] FAIL - The entity-class '" . $className . "' mapping is invalid:\n"); + foreach ($errorMessages AS $errorMessage) { + $output->write('* ' . $errorMessage . "\n"); + } + $output->write("\n"); + } + $exit += 1; + } else { + $output->write('[Mapping] OK - The mapping files are correct.' . "\n"); + } + + if (!$validator->schemaInSyncWithMetadata()) { + $output->write('[Database] FAIL - The database schema is not in sync with the current mapping file.' . "\n"); + $exit += 2; + } else { + $output->write('[Database] OK - The database schema is in sync with the mapping files.' . "\n"); + } + + return $exit; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php new file mode 100644 index 0000000..6907dde --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; + +class ConsoleRunner +{ + /** + * Run console with the given helperset. + * + * @param \Symfony\Component\Console\Helper\HelperSet $helperSet + * @return void + */ + static public function run(HelperSet $helperSet) + { + $cli = new Application('Doctrine Command Line Interface', \Doctrine\ORM\Version::VERSION); + $cli->setCatchExceptions(true); + $cli->setHelperSet($helperSet); + self::addCommands($cli); + $cli->run(); + } + + /** + * @param Application $cli + */ + static public function addCommands(Application $cli) + { + $cli->addCommands(array( + // DBAL Commands + new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(), + new \Doctrine\DBAL\Tools\Console\Command\ImportCommand(), + + // ORM Commands + new \Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand(), + new \Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand(), + new \Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand(), + new \Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand(), + new \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand(), + new \Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand(), + new \Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand(), + new \Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand(), + new \Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand(), + new \Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand(), + new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(), + new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(), + new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(), + new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(), + new \Doctrine\ORM\Tools\Console\Command\InfoCommand() + )); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Helper/EntityManagerHelper.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Helper/EntityManagerHelper.php new file mode 100644 index 0000000..c28622e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Helper/EntityManagerHelper.php @@ -0,0 +1,74 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Helper; + +use Symfony\Component\Console\Helper\Helper, + Doctrine\ORM\EntityManager; + +/** + * Doctrine CLI Connection Helper. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityManagerHelper extends Helper +{ + /** + * Doctrine ORM EntityManager + * @var EntityManager + */ + protected $_em; + + /** + * Constructor + * + * @param Connection $connection Doctrine Database Connection + */ + public function __construct(EntityManager $em) + { + $this->_em = $em; + } + + /** + * Retrieves Doctrine ORM EntityManager + * + * @return EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * @see Helper + */ + public function getName() + { + return 'entityManager'; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php new file mode 100644 index 0000000..2f69d89 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php @@ -0,0 +1,80 @@ +. +*/ + +namespace Doctrine\ORM\Tools\Console; + +/** + * Used by CLI Tools to restrict entity-based commands to given patterns. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class MetadataFilter extends \FilterIterator implements \Countable +{ + /** + * Filter Metadatas by one or more filter options. + * + * @param array $metadatas + * @param array|string $filter + * @return array + */ + static public function filter(array $metadatas, $filter) + { + $metadatas = new MetadataFilter(new \ArrayIterator($metadatas), $filter); + return iterator_to_array($metadatas); + } + + private $_filter = array(); + + public function __construct(\ArrayIterator $metadata, $filter) + { + $this->_filter = (array)$filter; + parent::__construct($metadata); + } + + public function accept() + { + if (count($this->_filter) == 0) { + return true; + } + + $it = $this->getInnerIterator(); + $metadata = $it->current(); + + foreach ($this->_filter AS $filter) { + if (strpos($metadata->name, $filter) !== false) { + return true; + } + } + return false; + } + + public function count() + { + return count($this->getInnerIterator()); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php new file mode 100644 index 0000000..93edebb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php @@ -0,0 +1,274 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Mapping\ClassMetadataInfo, + Doctrine\ORM\Tools\Export\Driver\AbstractExporter, + Doctrine\Common\Util\Inflector; + +/** + * Class to help with converting Doctrine 1 schema files to Doctrine 2 mapping files + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConvertDoctrine1Schema +{ + private $_legacyTypeMap = array( + // TODO: This list may need to be updated + 'clob' => 'text', + 'timestamp' => 'datetime', + 'enum' => 'string' + ); + + /** + * Constructor passes the directory or array of directories + * to convert the Doctrine 1 schema files from + * + * @param array $from + * @author Jonathan Wage + */ + public function __construct($from) + { + $this->_from = (array) $from; + } + + /** + * Get an array of ClassMetadataInfo instances from the passed + * Doctrine 1 schema + * + * @return array $metadatas An array of ClassMetadataInfo instances + */ + public function getMetadata() + { + $schema = array(); + foreach ($this->_from as $path) { + if (is_dir($path)) { + $files = glob($path . '/*.yml'); + foreach ($files as $file) { + $schema = array_merge($schema, (array) \Symfony\Component\Yaml\Yaml::parse($file)); + } + } else { + $schema = array_merge($schema, (array) \Symfony\Component\Yaml\Yaml::parse($path)); + } + } + + $metadatas = array(); + foreach ($schema as $className => $mappingInformation) { + $metadatas[] = $this->_convertToClassMetadataInfo($className, $mappingInformation); + } + + return $metadatas; + } + + private function _convertToClassMetadataInfo($className, $mappingInformation) + { + $metadata = new ClassMetadataInfo($className); + + $this->_convertTableName($className, $mappingInformation, $metadata); + $this->_convertColumns($className, $mappingInformation, $metadata); + $this->_convertIndexes($className, $mappingInformation, $metadata); + $this->_convertRelations($className, $mappingInformation, $metadata); + + return $metadata; + } + + private function _convertTableName($className, array $model, ClassMetadataInfo $metadata) + { + if (isset($model['tableName']) && $model['tableName']) { + $e = explode('.', $model['tableName']); + + if (count($e) > 1) { + $metadata->table['schema'] = $e[0]; + $metadata->table['name'] = $e[1]; + } else { + $metadata->table['name'] = $e[0]; + } + } + } + + private function _convertColumns($className, array $model, ClassMetadataInfo $metadata) + { + $id = false; + + if (isset($model['columns']) && $model['columns']) { + foreach ($model['columns'] as $name => $column) { + $fieldMapping = $this->_convertColumn($className, $name, $column, $metadata); + + if (isset($fieldMapping['id']) && $fieldMapping['id']) { + $id = true; + } + } + } + + if ( ! $id) { + $fieldMapping = array( + 'fieldName' => 'id', + 'columnName' => 'id', + 'type' => 'integer', + 'id' => true + ); + $metadata->mapField($fieldMapping); + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + } + } + + private function _convertColumn($className, $name, $column, ClassMetadataInfo $metadata) + { + if (is_string($column)) { + $string = $column; + $column = array(); + $column['type'] = $string; + } + if ( ! isset($column['name'])) { + $column['name'] = $name; + } + // check if a column alias was used (column_name as field_name) + if (preg_match("/(\w+)\sas\s(\w+)/i", $column['name'], $matches)) { + $name = $matches[1]; + $column['name'] = $name; + $column['alias'] = $matches[2]; + } + if (preg_match("/([a-zA-Z]+)\(([0-9]+)\)/", $column['type'], $matches)) { + $column['type'] = $matches[1]; + $column['length'] = $matches[2]; + } + $column['type'] = strtolower($column['type']); + // check if legacy column type (1.x) needs to be mapped to a 2.0 one + if (isset($this->_legacyTypeMap[$column['type']])) { + $column['type'] = $this->_legacyTypeMap[$column['type']]; + } + if ( ! \Doctrine\DBAL\Types\Type::hasType($column['type'])) { + throw ToolsException::couldNotMapDoctrine1Type($column['type']); + } + + $fieldMapping = array(); + if (isset($column['primary'])) { + $fieldMapping['id'] = true; + } + $fieldMapping['fieldName'] = isset($column['alias']) ? $column['alias'] : $name; + $fieldMapping['columnName'] = $column['name']; + $fieldMapping['type'] = $column['type']; + if (isset($column['length'])) { + $fieldMapping['length'] = $column['length']; + } + $allowed = array('precision', 'scale', 'unique', 'options', 'notnull', 'version'); + foreach ($column as $key => $value) { + if (in_array($key, $allowed)) { + $fieldMapping[$key] = $value; + } + } + + $metadata->mapField($fieldMapping); + + if (isset($column['autoincrement'])) { + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + } else if (isset($column['sequence'])) { + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE); + $definition = array( + 'sequenceName' => is_array($column['sequence']) ? $column['sequence']['name']:$column['sequence'] + ); + if (isset($column['sequence']['size'])) { + $definition['allocationSize'] = $column['sequence']['size']; + } + if (isset($column['sequence']['value'])) { + $definition['initialValue'] = $column['sequence']['value']; + } + $metadata->setSequenceGeneratorDefinition($definition); + } + return $fieldMapping; + } + + private function _convertIndexes($className, array $model, ClassMetadataInfo $metadata) + { + if (isset($model['indexes']) && $model['indexes']) { + foreach ($model['indexes'] as $name => $index) { + $type = (isset($index['type']) && $index['type'] == 'unique') + ? 'uniqueConstraints' : 'indexes'; + + $metadata->table[$type][$name] = array( + 'columns' => $index['fields'] + ); + } + } + } + + private function _convertRelations($className, array $model, ClassMetadataInfo $metadata) + { + if (isset($model['relations']) && $model['relations']) { + foreach ($model['relations'] as $name => $relation) { + if ( ! isset($relation['alias'])) { + $relation['alias'] = $name; + } + if ( ! isset($relation['class'])) { + $relation['class'] = $name; + } + if ( ! isset($relation['local'])) { + $relation['local'] = Inflector::tableize($relation['class']); + } + if ( ! isset($relation['foreign'])) { + $relation['foreign'] = 'id'; + } + if ( ! isset($relation['foreignAlias'])) { + $relation['foreignAlias'] = $className; + } + + if (isset($relation['refClass'])) { + $type = 'many'; + $foreignType = 'many'; + $joinColumns = array(); + } else { + $type = isset($relation['type']) ? $relation['type'] : 'one'; + $foreignType = isset($relation['foreignType']) ? $relation['foreignType'] : 'many'; + $joinColumns = array( + array( + 'name' => $relation['local'], + 'referencedColumnName' => $relation['foreign'], + 'onDelete' => isset($relation['onDelete']) ? $relation['onDelete'] : null, + ) + ); + } + + if ($type == 'one' && $foreignType == 'one') { + $method = 'mapOneToOne'; + } else if ($type == 'many' && $foreignType == 'many') { + $method = 'mapManyToMany'; + } else { + $method = 'mapOneToMany'; + } + + $associationMapping = array(); + $associationMapping['fieldName'] = $relation['alias']; + $associationMapping['targetEntity'] = $relation['class']; + $associationMapping['mappedBy'] = $relation['foreignAlias']; + $associationMapping['joinColumns'] = $joinColumns; + + $metadata->$method($associationMapping); + } + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php new file mode 100644 index 0000000..0548b96 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php @@ -0,0 +1,151 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Event\OnFlushEventArgs; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\UnitOfWork; + +/** + * Use this logger to dump the identity map during the onFlush event. This is useful for debugging + * weird UnitOfWork behavior with complex operations. + */ +class DebugUnitOfWorkListener +{ + private $file; + private $context; + + /** + * Pass a stream and contet information for the debugging session. + * + * The stream can be php://output to print to the screen. + * + * @param string $file + * @param string $context + */ + public function __construct($file = 'php://output', $context = '') + { + $this->file = $file; + $this->context = $context; + } + + public function onFlush(OnFlushEventArgs $args) + { + $this->dumpIdentityMap($args->getEntityManager()); + } + + /** + * Dump the contents of the identity map into a stream. + * + * @param EntityManager $em + * @return void + */ + public function dumpIdentityMap(EntityManager $em) + { + $uow = $em->getUnitOfWork(); + $identityMap = $uow->getIdentityMap(); + + $fh = fopen($this->file, "x+"); + if (count($identityMap) == 0) { + fwrite($fh, "Flush Operation [".$this->context."] - Empty identity map.\n"); + return; + } + + fwrite($fh, "Flush Operation [".$this->context."] - Dumping identity map:\n"); + foreach ($identityMap AS $className => $map) { + fwrite($fh, "Class: ". $className . "\n"); + foreach ($map AS $idHash => $entity) { + fwrite($fh, " Entity: " . $this->getIdString($entity, $uow) . " " . spl_object_hash($entity)."\n"); + fwrite($fh, " Associations:\n"); + + $cm = $em->getClassMetadata($className); + foreach ($cm->associationMappings AS $field => $assoc) { + fwrite($fh, " " . $field . " "); + $value = $cm->reflFields[$field]->getValue($entity); + + if ($assoc['type'] & ClassMetadata::TO_ONE) { + if ($value === null) { + fwrite($fh, " NULL\n"); + } else { + if ($value instanceof Proxy && !$value->__isInitialized__) { + fwrite($fh, "[PROXY] "); + } + + fwrite($fh, $this->getIdString($value, $uow) . " " . spl_object_hash($value) . "\n"); + } + } else { + $initialized = !($value instanceof PersistentCollection) || $value->isInitialized(); + if ($value === null) { + fwrite($fh, " NULL\n"); + } else if ($initialized) { + fwrite($fh, "[INITIALIZED] " . $this->getType($value). " " . count($value) . " elements\n"); + foreach ($value AS $obj) { + fwrite($fh, " " . $this->getIdString($obj, $uow) . " " . spl_object_hash($obj)."\n"); + } + } else { + fwrite($fh, "[PROXY] " . $this->getType($value) . " unknown element size\n"); + foreach ($value->unwrap() AS $obj) { + fwrite($fh, " " . $this->getIdString($obj, $uow) . " " . spl_object_hash($obj)."\n"); + } + } + } + } + } + } + fclose($fh); + } + + private function getType($var) + { + if (is_object($var)) { + $refl = new \ReflectionObject($var); + return $refl->getShortname(); + } else { + return gettype($var); + } + } + + private function getIdString($entity, $uow) + { + if ($uow->isInIdentityMap($entity)) { + $ids = $uow->getEntityIdentifier($entity); + $idstring = ""; + foreach ($ids AS $k => $v) { + $idstring .= $k."=".$v; + } + } else { + $idstring = "NEWOBJECT "; + } + + $state = $uow->getEntityState($entity); + if ($state == UnitOfWork::STATE_NEW) { + $idstring .= " [NEW]"; + } else if ($state == UnitOfWork::STATE_REMOVED) { + $idstring .= " [REMOVED]"; + } else if ($state == UnitOfWork::STATE_MANAGED) { + $idstring .= " [MANAGED]"; + } else if ($state == UnitOfwork::STATE_DETACHED) { + $idstring .= " [DETACHED]"; + } + + return $idstring; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php new file mode 100644 index 0000000..2603c22 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Mapping\ClassMetadataFactory; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * The DisconnectedClassMetadataFactory is used to create ClassMetadataInfo objects + * that do not require the entity class actually exist. This allows us to + * load some mapping information and use it to do things like generate code + * from the mapping information. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DisconnectedClassMetadataFactory extends ClassMetadataFactory +{ + public function getReflectionService() + { + return new \Doctrine\Common\Persistence\Mapping\StaticReflectionService; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php new file mode 100644 index 0000000..bbfcc13 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -0,0 +1,1129 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Mapping\ClassMetadataInfo, + Doctrine\ORM\Mapping\AssociationMapping, + Doctrine\Common\Util\Inflector; + +/** + * Generic class used to generate PHP5 entity classes from ClassMetadataInfo instances + * + * [php] + * $classes = $em->getClassMetadataFactory()->getAllMetadata(); + * + * $generator = new \Doctrine\ORM\Tools\EntityGenerator(); + * $generator->setGenerateAnnotations(true); + * $generator->setGenerateStubMethods(true); + * $generator->setRegenerateEntityIfExists(false); + * $generator->setUpdateEntityIfExists(true); + * $generator->generate($classes, '/path/to/generate/entities'); + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityGenerator +{ + /** + * @var bool + */ + private $_backupExisting = true; + + /** The extension to use for written php files */ + private $_extension = '.php'; + + /** Whether or not the current ClassMetadataInfo instance is new or old */ + private $_isNew = true; + + private $_staticReflection = array(); + + /** Number of spaces to use for indention in generated code */ + private $_numSpaces = 4; + + /** The actual spaces to use for indention */ + private $_spaces = ' '; + + /** The class all generated entities should extend */ + private $_classToExtend; + + /** Whether or not to generation annotations */ + private $_generateAnnotations = false; + + /** + * @var string + */ + private $_annotationsPrefix = ''; + + /** Whether or not to generated sub methods */ + private $_generateEntityStubMethods = false; + + /** Whether or not to update the entity class if it exists already */ + private $_updateEntityIfExists = false; + + /** Whether or not to re-generate entity class if it exists already */ + private $_regenerateEntityIfExists = false; + + private static $_classTemplate = +' + +use Doctrine\ORM\Mapping as ORM; + + + +{ + +}'; + + private static $_getMethodTemplate = +'/** + * + * + * @return + */ +public function () +{ +return $this->; +}'; + + private static $_setMethodTemplate = +'/** + * + * + * @param $ + * @return + */ +public function ($) +{ +$this-> = $; +return $this; +}'; + + private static $_addMethodTemplate = +'/** + * + * + * @param $ + * @return + */ +public function ($) +{ +$this->[] = $; +return $this; +}'; + + private static $_lifecycleCallbackMethodTemplate = +'/** + * @ + */ +public function () +{ +// Add your code here +}'; + + private static $_constructorMethodTemplate = +'public function __construct() +{ + +} +'; + + public function __construct() + { + if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) { + $this->_annotationsPrefix = 'ORM\\'; + } + } + + /** + * Generate and write entity classes for the given array of ClassMetadataInfo instances + * + * @param array $metadatas + * @param string $outputDirectory + * @return void + */ + public function generate(array $metadatas, $outputDirectory) + { + foreach ($metadatas as $metadata) { + $this->writeEntityClass($metadata, $outputDirectory); + } + } + + /** + * Generated and write entity class to disk for the given ClassMetadataInfo instance + * + * @param ClassMetadataInfo $metadata + * @param string $outputDirectory + * @return void + */ + public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory) + { + $path = $outputDirectory . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $metadata->name) . $this->_extension; + $dir = dirname($path); + + if ( ! is_dir($dir)) { + mkdir($dir, 0777, true); + } + + $this->_isNew = !file_exists($path) || (file_exists($path) && $this->_regenerateEntityIfExists); + + if ( ! $this->_isNew) { + $this->_parseTokensInEntityFile(file_get_contents($path)); + } else { + $this->_staticReflection[$metadata->name] = array('properties' => array(), 'methods' => array()); + } + + if ($this->_backupExisting && file_exists($path)) { + $backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . "~"; + if (!copy($path, $backupPath)) { + throw new \RuntimeException("Attempt to backup overwritten entity file but copy operation failed."); + } + } + + // If entity doesn't exist or we're re-generating the entities entirely + if ($this->_isNew) { + file_put_contents($path, $this->generateEntityClass($metadata)); + // If entity exists and we're allowed to update the entity class + } else if ( ! $this->_isNew && $this->_updateEntityIfExists) { + file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path)); + } + } + + /** + * Generate a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance + * + * @param ClassMetadataInfo $metadata + * @return string $code + */ + public function generateEntityClass(ClassMetadataInfo $metadata) + { + $placeHolders = array( + '', + '', + '', + '' + ); + + $replacements = array( + $this->_generateEntityNamespace($metadata), + $this->_generateEntityDocBlock($metadata), + $this->_generateEntityClassName($metadata), + $this->_generateEntityBody($metadata) + ); + + $code = str_replace($placeHolders, $replacements, self::$_classTemplate); + return str_replace('', $this->_spaces, $code); + } + + /** + * Generate the updated code for the given ClassMetadataInfo and entity at path + * + * @param ClassMetadataInfo $metadata + * @param string $path + * @return string $code; + */ + public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path) + { + $currentCode = file_get_contents($path); + + $body = $this->_generateEntityBody($metadata); + $body = str_replace('', $this->_spaces, $body); + $last = strrpos($currentCode, '}'); + + return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : ''). "}"; + } + + /** + * Set the number of spaces the exported class should have + * + * @param integer $numSpaces + * @return void + */ + public function setNumSpaces($numSpaces) + { + $this->_spaces = str_repeat(' ', $numSpaces); + $this->_numSpaces = $numSpaces; + } + + /** + * Set the extension to use when writing php files to disk + * + * @param string $extension + * @return void + */ + public function setExtension($extension) + { + $this->_extension = $extension; + } + + /** + * Set the name of the class the generated classes should extend from + * + * @return void + */ + public function setClassToExtend($classToExtend) + { + $this->_classToExtend = $classToExtend; + } + + /** + * Set whether or not to generate annotations for the entity + * + * @param bool $bool + * @return void + */ + public function setGenerateAnnotations($bool) + { + $this->_generateAnnotations = $bool; + } + + /** + * Set an annotation prefix. + * + * @param string $prefix + */ + public function setAnnotationPrefix($prefix) + { + $this->_annotationsPrefix = $prefix; + } + + /** + * Set whether or not to try and update the entity if it already exists + * + * @param bool $bool + * @return void + */ + public function setUpdateEntityIfExists($bool) + { + $this->_updateEntityIfExists = $bool; + } + + /** + * Set whether or not to regenerate the entity if it exists + * + * @param bool $bool + * @return void + */ + public function setRegenerateEntityIfExists($bool) + { + $this->_regenerateEntityIfExists = $bool; + } + + /** + * Set whether or not to generate stub methods for the entity + * + * @param bool $bool + * @return void + */ + public function setGenerateStubMethods($bool) + { + $this->_generateEntityStubMethods = $bool; + } + + /** + * Should an existing entity be backed up if it already exists? + */ + public function setBackupExisting($bool) + { + $this->_backupExisting = $bool; + } + + private function _generateEntityNamespace(ClassMetadataInfo $metadata) + { + if ($this->_hasNamespace($metadata)) { + return 'namespace ' . $this->_getNamespace($metadata) .';'; + } + } + + private function _generateEntityClassName(ClassMetadataInfo $metadata) + { + return 'class ' . $this->_getClassName($metadata) . + ($this->_extendsClass() ? ' extends ' . $this->_getClassToExtendName() : null); + } + + private function _generateEntityBody(ClassMetadataInfo $metadata) + { + $fieldMappingProperties = $this->_generateEntityFieldMappingProperties($metadata); + $associationMappingProperties = $this->_generateEntityAssociationMappingProperties($metadata); + $stubMethods = $this->_generateEntityStubMethods ? $this->_generateEntityStubMethods($metadata) : null; + $lifecycleCallbackMethods = $this->_generateEntityLifecycleCallbackMethods($metadata); + + $code = array(); + + if ($fieldMappingProperties) { + $code[] = $fieldMappingProperties; + } + + if ($associationMappingProperties) { + $code[] = $associationMappingProperties; + } + + $code[] = $this->_generateEntityConstructor($metadata); + + if ($stubMethods) { + $code[] = $stubMethods; + } + + if ($lifecycleCallbackMethods) { + $code[] = $lifecycleCallbackMethods; + } + + return implode("\n", $code); + } + + private function _generateEntityConstructor(ClassMetadataInfo $metadata) + { + if ($this->_hasMethod('__construct', $metadata)) { + return ''; + } + + $collections = array(); + + foreach ($metadata->associationMappings AS $mapping) { + if ($mapping['type'] & ClassMetadataInfo::TO_MANY) { + $collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();'; + } + } + + if ($collections) { + return $this->_prefixCodeWithSpaces(str_replace("", implode("\n".$this->_spaces, $collections), self::$_constructorMethodTemplate)); + } + + return ''; + } + + /** + * @todo this won't work if there is a namespace in brackets and a class outside of it. + * @param string $src + */ + private function _parseTokensInEntityFile($src) + { + $tokens = token_get_all($src); + $lastSeenNamespace = ""; + $lastSeenClass = false; + + $inNamespace = false; + $inClass = false; + for ($i = 0; $i < count($tokens); $i++) { + $token = $tokens[$i]; + if (in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) { + continue; + } + + if ($inNamespace) { + if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) { + $lastSeenNamespace .= $token[1]; + } else if (is_string($token) && in_array($token, array(';', '{'))) { + $inNamespace = false; + } + } + + if ($inClass) { + $inClass = false; + $lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1]; + $this->_staticReflection[$lastSeenClass]['properties'] = array(); + $this->_staticReflection[$lastSeenClass]['methods'] = array(); + } + + if ($token[0] == T_NAMESPACE) { + $lastSeenNamespace = ""; + $inNamespace = true; + } else if ($token[0] == T_CLASS) { + $inClass = true; + } else if ($token[0] == T_FUNCTION) { + if ($tokens[$i+2][0] == T_STRING) { + $this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1]; + } else if ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) { + $this->_staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+3][1]; + } + } else if (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) { + $this->_staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i+2][1], 1); + } + } + } + + private function _hasProperty($property, ClassMetadataInfo $metadata) + { + if ($this->_extendsClass()) { + // don't generate property if its already on the base class. + $reflClass = new \ReflectionClass($this->_getClassToExtend()); + if ($reflClass->hasProperty($property)) { + return true; + } + } + + return ( + isset($this->_staticReflection[$metadata->name]) && + in_array($property, $this->_staticReflection[$metadata->name]['properties']) + ); + } + + private function _hasMethod($method, ClassMetadataInfo $metadata) + { + if ($this->_extendsClass()) { + // don't generate method if its already on the base class. + $reflClass = new \ReflectionClass($this->_getClassToExtend()); + if ($reflClass->hasMethod($method)) { + return true; + } + } + + return ( + isset($this->_staticReflection[$metadata->name]) && + in_array($method, $this->_staticReflection[$metadata->name]['methods']) + ); + } + + private function _hasNamespace(ClassMetadataInfo $metadata) + { + return strpos($metadata->name, '\\') ? true : false; + } + + private function _extendsClass() + { + return $this->_classToExtend ? true : false; + } + + private function _getClassToExtend() + { + return $this->_classToExtend; + } + + private function _getClassToExtendName() + { + $refl = new \ReflectionClass($this->_getClassToExtend()); + + return '\\' . $refl->getName(); + } + + private function _getClassName(ClassMetadataInfo $metadata) + { + return ($pos = strrpos($metadata->name, '\\')) + ? substr($metadata->name, $pos + 1, strlen($metadata->name)) : $metadata->name; + } + + private function _getNamespace(ClassMetadataInfo $metadata) + { + return substr($metadata->name, 0, strrpos($metadata->name, '\\')); + } + + private function _generateEntityDocBlock(ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = '/**'; + $lines[] = ' * '.$metadata->name; + + if ($this->_generateAnnotations) { + $lines[] = ' *'; + + $methods = array( + '_generateTableAnnotation', + '_generateInheritanceAnnotation', + '_generateDiscriminatorColumnAnnotation', + '_generateDiscriminatorMapAnnotation' + ); + + foreach ($methods as $method) { + if ($code = $this->$method($metadata)) { + $lines[] = ' * ' . $code; + } + } + + if ($metadata->isMappedSuperclass) { + $lines[] = ' * @' . $this->_annotationsPrefix . 'MappedSuperClass'; + } else { + $lines[] = ' * @' . $this->_annotationsPrefix . 'Entity'; + } + + if ($metadata->customRepositoryClassName) { + $lines[count($lines) - 1] .= '(repositoryClass="' . $metadata->customRepositoryClassName . '")'; + } + + if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) { + $lines[] = ' * @' . $this->_annotationsPrefix . 'HasLifecycleCallbacks'; + } + } + + $lines[] = ' */'; + + return implode("\n", $lines); + } + + private function _generateTableAnnotation($metadata) + { + $table = array(); + + if (isset($metadata->table['schema'])) { + $table[] = 'schema="' . $metadata->table['schema'] . '"'; + } + + if (isset($metadata->table['name'])) { + $table[] = 'name="' . $metadata->table['name'] . '"'; + } + + if (isset($metadata->table['uniqueConstraints']) && $metadata->table['uniqueConstraints']) { + $constraints = $this->_generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']); + $table[] = 'uniqueConstraints={' . $constraints . '}'; + } + + if (isset($metadata->table['indexes']) && $metadata->table['indexes']) { + $constraints = $this->_generateTableConstraints('Index', $metadata->table['indexes']); + $table[] = 'indexes={' . $constraints . '}'; + } + + return '@' . $this->_annotationsPrefix . 'Table(' . implode(', ', $table) . ')'; + } + + private function _generateTableConstraints($constraintName, $constraints) + { + $annotations = array(); + foreach ($constraints as $name => $constraint) { + $columns = array(); + foreach ($constraint['columns'] as $column) { + $columns[] = '"' . $column . '"'; + } + $annotations[] = '@' . $this->_annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})'; + } + return implode(', ', $annotations); + } + + private function _generateInheritanceAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + return '@' . $this->_annotationsPrefix . 'InheritanceType("'.$this->_getInheritanceTypeString($metadata->inheritanceType).'")'; + } + } + + private function _generateDiscriminatorColumnAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $discrColumn = $metadata->discriminatorValue; + $columnDefinition = 'name="' . $discrColumn['name'] + . '", type="' . $discrColumn['type'] + . '", length=' . $discrColumn['length']; + + return '@' . $this->_annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')'; + } + } + + private function _generateDiscriminatorMapAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $inheritanceClassMap = array(); + + foreach ($metadata->discriminatorMap as $type => $class) { + $inheritanceClassMap[] .= '"' . $type . '" = "' . $class . '"'; + } + + return '@' . $this->_annotationsPrefix . 'DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})'; + } + } + + private function _generateEntityStubMethods(ClassMetadataInfo $metadata) + { + $methods = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if ( ! isset($fieldMapping['id']) || ! $fieldMapping['id'] || $metadata->generatorType == ClassMetadataInfo::GENERATOR_TYPE_NONE) { + if ($code = $this->_generateEntityStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'])) { + $methods[] = $code; + } + } + + if ($code = $this->_generateEntityStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'])) { + $methods[] = $code; + } + } + + foreach ($metadata->associationMappings as $associationMapping) { + if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + $nullable = $this->_isAssociationIsNullable($associationMapping) ? 'null' : null; + if ($code = $this->_generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) { + $methods[] = $code; + } + if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { + $methods[] = $code; + } + } else if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) { + if ($code = $this->_generateEntityStubMethod($metadata, 'add', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { + $methods[] = $code; + } + if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], 'Doctrine\Common\Collections\Collection')) { + $methods[] = $code; + } + } + } + + return implode("\n\n", $methods); + } + + private function _isAssociationIsNullable($associationMapping) + { + if (isset($associationMapping['id']) && $associationMapping['id']) { + return false; + } + if (isset($associationMapping['joinColumns'])) { + $joinColumns = $associationMapping['joinColumns']; + } else { + //@todo thereis no way to retreive targetEntity metadata + $joinColumns = array(); + } + foreach ($joinColumns as $joinColumn) { + if(isset($joinColumn['nullable']) && !$joinColumn['nullable']) { + return false; + } + } + return true; + } + + private function _generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata) + { + if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) { + $methods = array(); + + foreach ($metadata->lifecycleCallbacks as $name => $callbacks) { + foreach ($callbacks as $callback) { + if ($code = $this->_generateLifecycleCallbackMethod($name, $callback, $metadata)) { + $methods[] = $code; + } + } + } + + return implode("\n\n", $methods); + } + + return ""; + } + + private function _generateEntityAssociationMappingProperties(ClassMetadataInfo $metadata) + { + $lines = array(); + + foreach ($metadata->associationMappings as $associationMapping) { + if ($this->_hasProperty($associationMapping['fieldName'], $metadata)) { + continue; + } + + $lines[] = $this->_generateAssociationMappingPropertyDocBlock($associationMapping, $metadata); + $lines[] = $this->_spaces . 'private $' . $associationMapping['fieldName'] + . ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n"; + } + + return implode("\n", $lines); + } + + private function _generateEntityFieldMappingProperties(ClassMetadataInfo $metadata) + { + $lines = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if ($this->_hasProperty($fieldMapping['fieldName'], $metadata) || + $metadata->isInheritedField($fieldMapping['fieldName'])) { + continue; + } + + $lines[] = $this->_generateFieldMappingPropertyDocBlock($fieldMapping, $metadata); + $lines[] = $this->_spaces . 'private $' . $fieldMapping['fieldName'] + . (isset($fieldMapping['default']) ? ' = ' . var_export($fieldMapping['default'], true) : null) . ";\n"; + } + + return implode("\n", $lines); + } + + private function _generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null) + { + if ($type == "add") { + $addMethod = explode("\\", $typeHint); + $addMethod = end($addMethod); + $methodName = $type . $addMethod; + } else { + $methodName = $type . Inflector::classify($fieldName); + } + + if ($this->_hasMethod($methodName, $metadata)) { + return; + } + $this->_staticReflection[$metadata->name]['methods'][] = $methodName; + + $var = sprintf('_%sMethodTemplate', $type); + $template = self::$$var; + + $variableType = $typeHint ? $typeHint . ' ' : null; + + $types = \Doctrine\DBAL\Types\Type::getTypesMap(); + $methodTypeHint = $typeHint && ! isset($types[$typeHint]) ? '\\' . $typeHint . ' ' : null; + + $replacements = array( + '' => ucfirst($type) . ' ' . $fieldName, + '' => $methodTypeHint, + '' => $variableType, + '' => Inflector::camelize($fieldName), + '' => $methodName, + '' => $fieldName, + '' => ($defaultValue !== null ) ? (' = '.$defaultValue) : '', + '' => $this->_getClassName($metadata) + ); + + $method = str_replace( + array_keys($replacements), + array_values($replacements), + $template + ); + + return $this->_prefixCodeWithSpaces($method); + } + + private function _generateLifecycleCallbackMethod($name, $methodName, $metadata) + { + if ($this->_hasMethod($methodName, $metadata)) { + return; + } + $this->_staticReflection[$metadata->name]['methods'][] = $methodName; + + $replacements = array( + '' => $this->_annotationsPrefix . ucfirst($name), + '' => $methodName, + ); + + $method = str_replace( + array_keys($replacements), + array_values($replacements), + self::$_lifecycleCallbackMethodTemplate + ); + + return $this->_prefixCodeWithSpaces($method); + } + + private function _generateJoinColumnAnnotation(array $joinColumn) + { + $joinColumnAnnot = array(); + + if (isset($joinColumn['name'])) { + $joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"'; + } + + if (isset($joinColumn['referencedColumnName'])) { + $joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"'; + } + + if (isset($joinColumn['unique']) && $joinColumn['unique']) { + $joinColumnAnnot[] = 'unique=' . ($joinColumn['unique'] ? 'true' : 'false'); + } + + if (isset($joinColumn['nullable'])) { + $joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false'); + } + + if (isset($joinColumn['onDelete'])) { + $joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"'); + } + + if (isset($joinColumn['columnDefinition'])) { + $joinColumnAnnot[] = 'columnDefinition="' . $joinColumn['columnDefinition'] . '"'; + } + + return '@' . $this->_annotationsPrefix . 'JoinColumn(' . implode(', ', $joinColumnAnnot) . ')'; + } + + private function _generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = $this->_spaces . '/**'; + + if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) { + $lines[] = $this->_spaces . ' * @var \Doctrine\Common\Collections\ArrayCollection'; + } else { + $lines[] = $this->_spaces . ' * @var ' . $associationMapping['targetEntity']; + } + + if ($this->_generateAnnotations) { + $lines[] = $this->_spaces . ' *'; + + if (isset($associationMapping['id']) && $associationMapping['id']) { + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Id'; + + if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")'; + } + } + + $type = null; + switch ($associationMapping['type']) { + case ClassMetadataInfo::ONE_TO_ONE: + $type = 'OneToOne'; + break; + case ClassMetadataInfo::MANY_TO_ONE: + $type = 'ManyToOne'; + break; + case ClassMetadataInfo::ONE_TO_MANY: + $type = 'OneToMany'; + break; + case ClassMetadataInfo::MANY_TO_MANY: + $type = 'ManyToMany'; + break; + } + $typeOptions = array(); + + if (isset($associationMapping['targetEntity'])) { + $typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"'; + } + + if (isset($associationMapping['inversedBy'])) { + $typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"'; + } + + if (isset($associationMapping['mappedBy'])) { + $typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"'; + } + + if ($associationMapping['cascade']) { + $cascades = array(); + + if ($associationMapping['isCascadePersist']) $cascades[] = '"persist"'; + if ($associationMapping['isCascadeRemove']) $cascades[] = '"remove"'; + if ($associationMapping['isCascadeDetach']) $cascades[] = '"detach"'; + if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"'; + if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"'; + + $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}'; + } + + if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) { + $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false'); + } + + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')'; + + if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) { + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'JoinColumns({'; + + $joinColumnsLines = array(); + + foreach ($associationMapping['joinColumns'] as $joinColumn) { + if ($joinColumnAnnot = $this->_generateJoinColumnAnnotation($joinColumn)) { + $joinColumnsLines[] = $this->_spaces . ' * ' . $joinColumnAnnot; + } + } + + $lines[] = implode(",\n", $joinColumnsLines); + $lines[] = $this->_spaces . ' * })'; + } + + if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) { + $joinTable = array(); + $joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"'; + + if (isset($associationMapping['joinTable']['schema'])) { + $joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"'; + } + + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'JoinTable(' . implode(', ', $joinTable) . ','; + $lines[] = $this->_spaces . ' * joinColumns={'; + + foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) { + $lines[] = $this->_spaces . ' * ' . $this->_generateJoinColumnAnnotation($joinColumn); + } + + $lines[] = $this->_spaces . ' * },'; + $lines[] = $this->_spaces . ' * inverseJoinColumns={'; + + foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $lines[] = $this->_spaces . ' * ' . $this->_generateJoinColumnAnnotation($joinColumn); + } + + $lines[] = $this->_spaces . ' * }'; + $lines[] = $this->_spaces . ' * )'; + } + + if (isset($associationMapping['orderBy'])) { + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'OrderBy({'; + + foreach ($associationMapping['orderBy'] as $name => $direction) { + $lines[] = $this->_spaces . ' * "' . $name . '"="' . $direction . '",'; + } + + $lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1); + $lines[] = $this->_spaces . ' * })'; + } + } + + $lines[] = $this->_spaces . ' */'; + + return implode("\n", $lines); + } + + private function _generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = $this->_spaces . '/**'; + $lines[] = $this->_spaces . ' * @var ' . $fieldMapping['type'] . ' $' . $fieldMapping['fieldName']; + + if ($this->_generateAnnotations) { + $lines[] = $this->_spaces . ' *'; + + $column = array(); + if (isset($fieldMapping['columnName'])) { + $column[] = 'name="' . $fieldMapping['columnName'] . '"'; + } + + if (isset($fieldMapping['type'])) { + $column[] = 'type="' . $fieldMapping['type'] . '"'; + } + + if (isset($fieldMapping['length'])) { + $column[] = 'length=' . $fieldMapping['length']; + } + + if (isset($fieldMapping['precision'])) { + $column[] = 'precision=' . $fieldMapping['precision']; + } + + if (isset($fieldMapping['scale'])) { + $column[] = 'scale=' . $fieldMapping['scale']; + } + + if (isset($fieldMapping['nullable'])) { + $column[] = 'nullable=' . var_export($fieldMapping['nullable'], true); + } + + if (isset($fieldMapping['columnDefinition'])) { + $column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"'; + } + + if (isset($fieldMapping['unique'])) { + $column[] = 'unique=' . var_export($fieldMapping['unique'], true); + } + + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Column(' . implode(', ', $column) . ')'; + + if (isset($fieldMapping['id']) && $fieldMapping['id']) { + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Id'; + + if ($generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $lines[] = $this->_spaces.' * @' . $this->_annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")'; + } + + if ($metadata->sequenceGeneratorDefinition) { + $sequenceGenerator = array(); + + if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) { + $sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"'; + } + + if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) { + $sequenceGenerator[] = 'allocationSize=' . $metadata->sequenceGeneratorDefinition['allocationSize']; + } + + if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) { + $sequenceGenerator[] = 'initialValue=' . $metadata->sequenceGeneratorDefinition['initialValue']; + } + + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')'; + } + } + + if (isset($fieldMapping['version']) && $fieldMapping['version']) { + $lines[] = $this->_spaces . ' * @' . $this->_annotationsPrefix . 'Version'; + } + } + + $lines[] = $this->_spaces . ' */'; + + return implode("\n", $lines); + } + + private function _prefixCodeWithSpaces($code, $num = 1) + { + $lines = explode("\n", $code); + + foreach ($lines as $key => $value) { + $lines[$key] = str_repeat($this->_spaces, $num) . $lines[$key]; + } + + return implode("\n", $lines); + } + + private function _getInheritanceTypeString($type) + { + switch ($type) { + case ClassMetadataInfo::INHERITANCE_TYPE_NONE: + return 'NONE'; + + case ClassMetadataInfo::INHERITANCE_TYPE_JOINED: + return 'JOINED'; + + case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE: + return 'SINGLE_TABLE'; + + case ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS: + return 'PER_CLASS'; + + default: + throw new \InvalidArgumentException('Invalid provided InheritanceType: ' . $type); + } + } + + private function _getChangeTrackingPolicyString($policy) + { + switch ($policy) { + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT: + return 'DEFERRED_IMPLICIT'; + + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT: + return 'DEFERRED_EXPLICIT'; + + case ClassMetadataInfo::CHANGETRACKING_NOTIFY: + return 'NOTIFY'; + + default: + throw new \InvalidArgumentException('Invalid provided ChangeTrackingPolicy: ' . $policy); + } + } + + private function _getIdGeneratorTypeString($type) + { + switch ($type) { + case ClassMetadataInfo::GENERATOR_TYPE_AUTO: + return 'AUTO'; + + case ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE: + return 'SEQUENCE'; + + case ClassMetadataInfo::GENERATOR_TYPE_TABLE: + return 'TABLE'; + + case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY: + return 'IDENTITY'; + + case ClassMetadataInfo::GENERATOR_TYPE_NONE: + return 'NONE'; + + default: + throw new \InvalidArgumentException('Invalid provided IdGeneratorType: ' . $type); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityRepositoryGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityRepositoryGenerator.php new file mode 100644 index 0000000..74740db --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityRepositoryGenerator.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +/** + * Class to generate entity repository classes + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityRepositoryGenerator +{ + protected static $_template = +'; + +use Doctrine\ORM\EntityRepository; + +/** + * + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class extends EntityRepository +{ +}'; + + public function generateEntityRepositoryClass($fullClassName) + { + $namespace = substr($fullClassName, 0, strrpos($fullClassName, '\\')); + $className = substr($fullClassName, strrpos($fullClassName, '\\') + 1, strlen($fullClassName)); + + $variables = array( + '' => $namespace, + '' => $className + ); + return str_replace(array_keys($variables), array_values($variables), self::$_template); + } + + public function writeEntityRepositoryClass($fullClassName, $outputDirectory) + { + $code = $this->generateEntityRepositoryClass($fullClassName); + + $path = $outputDirectory . DIRECTORY_SEPARATOR + . str_replace('\\', \DIRECTORY_SEPARATOR, $fullClassName) . '.php'; + $dir = dirname($path); + + if ( ! is_dir($dir)) { + mkdir($dir, 0777, true); + } + + if ( ! file_exists($path)) { + file_put_contents($path, $code); + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php new file mode 100644 index 0000000..02d04a2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php @@ -0,0 +1,65 @@ +. +*/ + +namespace Doctrine\ORM\Tools\Event; + +use Doctrine\DBAL\Schema\Schema; +use Doctrine\ORM\EntityManager; + +/** + * Event Args used for the Events::postGenerateSchema event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class GenerateSchemaEventArgs extends \Doctrine\Common\EventArgs +{ + private $_em = null; + private $_schema = null; + + /** + * @param ClassMetadata $classMetadata + * @param Schema $schema + * @param Table $classTable + */ + public function __construct(EntityManager $em, Schema $schema) + { + $this->_em = $em; + $this->_schema = $schema; + } + + /** + * @return EntityManager + */ + public function getEntityManager() { + return $this->_em; + } + + /** + * @return Schema + */ + public function getSchema() { + return $this->_schema; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php new file mode 100644 index 0000000..54aa6e7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php @@ -0,0 +1,75 @@ +. +*/ + +namespace Doctrine\ORM\Tools\Event; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Table; + +/** + * Event Args used for the Events::postGenerateSchemaTable event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + */ +class GenerateSchemaTableEventArgs extends \Doctrine\Common\EventArgs +{ + private $_classMetadata = null; + private $_schema = null; + private $_classTable = null; + + /** + * @param ClassMetadata $classMetadata + * @param Schema $schema + * @param Table $classTable + */ + public function __construct(ClassMetadata $classMetadata, Schema $schema, Table $classTable) + { + $this->_classMetadata = $classMetadata; + $this->_schema = $schema; + $this->_classTable = $classTable; + } + + /** + * @return ClassMetadata + */ + public function getClassMetadata() { + return $this->_classMetadata; + } + + /** + * @return Schema + */ + public function getSchema() { + return $this->_schema; + } + + /** + * @return Table + */ + public function getClassTable() { + return $this->_classTable; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php new file mode 100644 index 0000000..2b528a2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export; + +use Doctrine\ORM\Tools\Export\ExportException, + Doctrine\ORM\EntityManager; + +/** + * Class used for converting your mapping information between the + * supported formats: yaml, xml, and php/annotation. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Jonathan Wage + */ +class ClassMetadataExporter +{ + private static $_exporterDrivers = array( + 'xml' => 'Doctrine\ORM\Tools\Export\Driver\XmlExporter', + 'yaml' => 'Doctrine\ORM\Tools\Export\Driver\YamlExporter', + 'yml' => 'Doctrine\ORM\Tools\Export\Driver\YamlExporter', + 'php' => 'Doctrine\ORM\Tools\Export\Driver\PhpExporter', + 'annotation' => 'Doctrine\ORM\Tools\Export\Driver\AnnotationExporter' + ); + + /** + * Register a new exporter driver class under a specified name + * + * @param string $name + * @param string $class + */ + public static function registerExportDriver($name, $class) + { + self::$_exporterDrivers[$name] = $class; + } + + /** + * Get a exporter driver instance + * + * @param string $type The type to get (yml, xml, etc.) + * @param string $source The directory where the exporter will export to + * @return AbstractExporter $exporter + */ + public function getExporter($type, $dest = null) + { + if ( ! isset(self::$_exporterDrivers[$type])) { + throw ExportException::invalidExporterDriverType($type); + } + + $class = self::$_exporterDrivers[$type]; + + return new $class($dest); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php new file mode 100644 index 0000000..41ec6fd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php @@ -0,0 +1,217 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Tools\Export\ExportException; + +/** + * Abstract base class which is to be used for the Exporter drivers + * which can be found in \Doctrine\ORM\Tools\Export\Driver + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Jonathan Wage + */ +abstract class AbstractExporter +{ + protected $_metadata = array(); + protected $_outputDir; + protected $_extension; + protected $_overwriteExistingFiles = false; + + public function __construct($dir = null) + { + $this->_outputDir = $dir; + } + + public function setOverwriteExistingFiles($overwrite) + { + $this->_overwriteExistingFiles = $overwrite; + } + + /** + * Converts a single ClassMetadata instance to the exported format + * and returns it + * + * @param ClassMetadataInfo $metadata + * @return mixed $exported + */ + abstract public function exportClassMetadata(ClassMetadataInfo $metadata); + + /** + * Set the array of ClassMetadataInfo instances to export + * + * @param array $metadata + * @return void + */ + public function setMetadata(array $metadata) + { + $this->_metadata = $metadata; + } + + /** + * Get the extension used to generated the path to a class + * + * @return string $extension + */ + public function getExtension() + { + return $this->_extension; + } + + /** + * Set the directory to output the mapping files to + * + * [php] + * $exporter = new YamlExporter($metadata); + * $exporter->setOutputDir(__DIR__ . '/yaml'); + * $exporter->export(); + * + * @param string $dir + * @return void + */ + public function setOutputDir($dir) + { + $this->_outputDir = $dir; + } + + /** + * Export each ClassMetadata instance to a single Doctrine Mapping file + * named after the entity + * + * @return void + */ + public function export() + { + if ( ! is_dir($this->_outputDir)) { + mkdir($this->_outputDir, 0777, true); + } + + foreach ($this->_metadata as $metadata) { + //In case output is returned, write it to a file, skip otherwise + if($output = $this->exportClassMetadata($metadata)){ + $path = $this->_generateOutputPath($metadata); + $dir = dirname($path); + if ( ! is_dir($dir)) { + mkdir($dir, 0777, true); + } + if (file_exists($path) && !$this->_overwriteExistingFiles) { + throw ExportException::attemptOverwriteExistingFile($path); + } + file_put_contents($path, $output); + } + } + } + + /** + * Generate the path to write the class for the given ClassMetadataInfo instance + * + * @param ClassMetadataInfo $metadata + * @return string $path + */ + protected function _generateOutputPath(ClassMetadataInfo $metadata) + { + return $this->_outputDir . '/' . str_replace('\\', '.', $metadata->name) . $this->_extension; + } + + /** + * Set the directory to output the mapping files to + * + * [php] + * $exporter = new YamlExporter($metadata, __DIR__ . '/yaml'); + * $exporter->setExtension('.yml'); + * $exporter->export(); + * + * @param string $extension + * @return void + */ + public function setExtension($extension) + { + $this->_extension = $extension; + } + + protected function _getInheritanceTypeString($type) + { + switch ($type) + { + case ClassMetadataInfo::INHERITANCE_TYPE_NONE: + return 'NONE'; + break; + + case ClassMetadataInfo::INHERITANCE_TYPE_JOINED: + return 'JOINED'; + break; + + case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE: + return 'SINGLE_TABLE'; + break; + + case ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS: + return 'PER_CLASS'; + break; + } + } + + protected function _getChangeTrackingPolicyString($policy) + { + switch ($policy) + { + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT: + return 'DEFERRED_IMPLICIT'; + break; + + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT: + return 'DEFERRED_EXPLICIT'; + break; + + case ClassMetadataInfo::CHANGETRACKING_NOTIFY: + return 'NOTIFY'; + break; + } + } + + protected function _getIdGeneratorTypeString($type) + { + switch ($type) + { + case ClassMetadataInfo::GENERATOR_TYPE_AUTO: + return 'AUTO'; + break; + + case ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE: + return 'SEQUENCE'; + break; + + case ClassMetadataInfo::GENERATOR_TYPE_TABLE: + return 'TABLE'; + break; + + case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY: + return 'IDENTITY'; + break; + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php new file mode 100644 index 0000000..5053290 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo, + Doctrine\ORM\Mapping\AssociationMapping, + Doctrine\ORM\Tools\EntityGenerator; + +/** + * ClassMetadata exporter for PHP classes with annotations + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Jonathan Wage + */ +class AnnotationExporter extends AbstractExporter +{ + protected $_extension = '.php'; + private $_entityGenerator; + + /** + * Converts a single ClassMetadata instance to the exported format + * and returns it + * + * @param ClassMetadataInfo $metadata + * @return string $exported + */ + public function exportClassMetadata(ClassMetadataInfo $metadata) + { + if ( ! $this->_entityGenerator) { + throw new \RuntimeException('For the AnnotationExporter you must set an EntityGenerator instance with the setEntityGenerator() method.'); + } + $this->_entityGenerator->setGenerateAnnotations(true); + $this->_entityGenerator->setGenerateStubMethods(false); + $this->_entityGenerator->setRegenerateEntityIfExists(false); + $this->_entityGenerator->setUpdateEntityIfExists(false); + + return $this->_entityGenerator->generateEntityClass($metadata); + } + + protected function _generateOutputPath(ClassMetadataInfo $metadata) + { + return $this->_outputDir . '/' . str_replace('\\', '/', $metadata->name) . $this->_extension; + } + + public function setEntityGenerator(EntityGenerator $entityGenerator) + { + $this->_entityGenerator = $entityGenerator; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php new file mode 100644 index 0000000..5332edb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php @@ -0,0 +1,169 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * ClassMetadata exporter for PHP code + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Jonathan Wage + */ +class PhpExporter extends AbstractExporter +{ + protected $_extension = '.php'; + + /** + * Converts a single ClassMetadata instance to the exported format + * and returns it + * + * @param ClassMetadataInfo $metadata + * @return mixed $exported + */ + public function exportClassMetadata(ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = 'isMappedSuperclass) { + $lines[] = '$metadata->isMappedSuperclass = true;'; + } + + if ($metadata->inheritanceType) { + $lines[] = '$metadata->setInheritanceType(ClassMetadataInfo::INHERITANCE_TYPE_' . $this->_getInheritanceTypeString($metadata->inheritanceType) . ');'; + } + + if ($metadata->customRepositoryClassName) { + $lines[] = "\$metadata->customRepositoryClassName = '" . $metadata->customRepositoryClassName . "';"; + } + + if ($metadata->table) { + $lines[] = '$metadata->setPrimaryTable(' . $this->_varExport($metadata->table) . ');'; + } + + if ($metadata->discriminatorColumn) { + $lines[] = '$metadata->setDiscriminatorColumn(' . $this->_varExport($metadata->discriminatorColumn) . ');'; + } + + if ($metadata->discriminatorMap) { + $lines[] = '$metadata->setDiscriminatorMap(' . $this->_varExport($metadata->discriminatorMap) . ');'; + } + + if ($metadata->changeTrackingPolicy) { + $lines[] = '$metadata->setChangeTrackingPolicy(ClassMetadataInfo::CHANGETRACKING_' . $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy) . ');'; + } + + if ($metadata->lifecycleCallbacks) { + foreach ($metadata->lifecycleCallbacks as $event => $callbacks) { + foreach ($callbacks as $callback) { + $lines[] = "\$metadata->addLifecycleCallback('$callback', '$event');"; + } + } + } + + foreach ($metadata->fieldMappings as $fieldMapping) { + $lines[] = '$metadata->mapField(' . $this->_varExport($fieldMapping) . ');'; + } + + if ( ! $metadata->isIdentifierComposite && $generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $lines[] = '$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_' . $generatorType . ');'; + } + + foreach ($metadata->associationMappings as $associationMapping) { + $cascade = array('remove', 'persist', 'refresh', 'merge', 'detach'); + foreach ($cascade as $key => $value) { + if ( ! $associationMapping['isCascade'.ucfirst($value)]) { + unset($cascade[$key]); + } + } + $associationMappingArray = array( + 'fieldName' => $associationMapping['fieldName'], + 'targetEntity' => $associationMapping['targetEntity'], + 'cascade' => $cascade, + ); + + if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + $method = 'mapOneToOne'; + $oneToOneMappingArray = array( + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'joinColumns' => $associationMapping['joinColumns'], + 'orphanRemoval' => $associationMapping['orphanRemoval'], + ); + + $associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray); + } else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { + $method = 'mapOneToMany'; + $potentialAssociationMappingIndexes = array( + 'mappedBy', + 'orphanRemoval', + 'orderBy', + ); + foreach ($potentialAssociationMappingIndexes as $index) { + if (isset($associationMapping[$index])) { + $oneToManyMappingArray[$index] = $associationMapping[$index]; + } + } + $associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray); + } else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) { + $method = 'mapManyToMany'; + $potentialAssociationMappingIndexes = array( + 'mappedBy', + 'joinTable', + 'orderBy', + ); + foreach ($potentialAssociationMappingIndexes as $index) { + if (isset($associationMapping[$index])) { + $manyToManyMappingArray[$index] = $associationMapping[$index]; + } + } + $associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray); + } + + $lines[] = '$metadata->' . $method . '(' . $this->_varExport($associationMappingArray) . ');'; + } + + return implode("\n", $lines); + } + + protected function _varExport($var) + { + $export = var_export($var, true); + $export = str_replace("\n", PHP_EOL . str_repeat(' ', 8), $export); + $export = str_replace(' ', ' ', $export); + $export = str_replace('array (', 'array(', $export); + $export = str_replace('array( ', 'array(', $export); + $export = str_replace(',)', ')', $export); + $export = str_replace(', )', ')', $export); + $export = str_replace(' ', ' ', $export); + + return $export; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php new file mode 100644 index 0000000..2cad8a4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php @@ -0,0 +1,320 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * ClassMetadata exporter for Doctrine XML mapping files + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Jonathan Wage + */ +class XmlExporter extends AbstractExporter +{ + protected $_extension = '.dcm.xml'; + + /** + * Converts a single ClassMetadata instance to the exported format + * and returns it + * + * @param ClassMetadataInfo $metadata + * @return mixed $exported + */ + public function exportClassMetadata(ClassMetadataInfo $metadata) + { + $xml = new \SimpleXmlElement(""); + + /*$xml->addAttribute('xmlns', 'http://doctrine-project.org/schemas/orm/doctrine-mapping'); + $xml->addAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + $xml->addAttribute('xsi:schemaLocation', 'http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd');*/ + + if ($metadata->isMappedSuperclass) { + $root = $xml->addChild('mapped-superclass'); + } else { + $root = $xml->addChild('entity'); + } + + if ($metadata->customRepositoryClassName) { + $root->addAttribute('repository-class', $metadata->customRepositoryClassName); + } + + $root->addAttribute('name', $metadata->name); + + if (isset($metadata->table['name'])) { + $root->addAttribute('table', $metadata->table['name']); + } + + if (isset($metadata->table['schema'])) { + $root->addAttribute('schema', $metadata->table['schema']); + } + + if (isset($metadata->table['inheritance-type'])) { + $root->addAttribute('inheritance-type', $metadata->table['inheritance-type']); + } + + if ($metadata->discriminatorColumn) { + $discriminatorColumnXml = $root->addChild('discriminiator-column'); + $discriminatorColumnXml->addAttribute('name', $metadata->discriminatorColumn['name']); + $discriminatorColumnXml->addAttribute('type', $metadata->discriminatorColumn['type']); + $discriminatorColumnXml->addAttribute('length', $metadata->discriminatorColumn['length']); + } + + if ($metadata->discriminatorMap) { + $discriminatorMapXml = $root->addChild('discriminator-map'); + foreach ($metadata->discriminatorMap as $value => $className) { + $discriminatorMappingXml = $discriminatorMapXml->addChild('discriminator-mapping'); + $discriminatorMappingXml->addAttribute('value', $value); + $discriminatorMappingXml->addAttribute('class', $className); + } + } + + $root->addChild('change-tracking-policy', $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy)); + + if (isset($metadata->table['indexes'])) { + $indexesXml = $root->addChild('indexes'); + + foreach ($metadata->table['indexes'] as $name => $index) { + $indexXml = $indexesXml->addChild('index'); + $indexXml->addAttribute('name', $name); + $indexXml->addAttribute('columns', implode(',', $index['columns'])); + } + } + + if (isset($metadata->table['uniqueConstraints'])) { + $uniqueConstraintsXml = $root->addChild('unique-constraints'); + + foreach ($metadata->table['uniqueConstraints'] as $unique) { + $uniqueConstraintXml = $uniqueConstraintsXml->addChild('unique-constraint'); + $uniqueConstraintXml->addAttribute('name', $unique['name']); + $uniqueConstraintXml->addAttribute('columns', implode(',', $unique['columns'])); + } + } + + $fields = $metadata->fieldMappings; + + $id = array(); + foreach ($fields as $name => $field) { + if (isset($field['id']) && $field['id']) { + $id[$name] = $field; + unset($fields[$name]); + } + } + + if ( ! $metadata->isIdentifierComposite && $idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $id[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType; + } + + if ($id) { + foreach ($id as $field) { + $idXml = $root->addChild('id'); + $idXml->addAttribute('name', $field['fieldName']); + $idXml->addAttribute('type', $field['type']); + if (isset($field['columnName'])) { + $idXml->addAttribute('column', $field['columnName']); + } + if (isset($field['associationKey']) && $field['associationKey']) { + $idXml->addAttribute('association-key', 'true'); + } + if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $generatorXml = $idXml->addChild('generator'); + $generatorXml->addAttribute('strategy', $idGeneratorType); + } + } + } + + if ($fields) { + foreach ($fields as $field) { + $fieldXml = $root->addChild('field'); + $fieldXml->addAttribute('name', $field['fieldName']); + $fieldXml->addAttribute('type', $field['type']); + if (isset($field['columnName'])) { + $fieldXml->addAttribute('column', $field['columnName']); + } + if (isset($field['length'])) { + $fieldXml->addAttribute('length', $field['length']); + } + if (isset($field['precision'])) { + $fieldXml->addAttribute('precision', $field['precision']); + } + if (isset($field['scale'])) { + $fieldXml->addAttribute('scale', $field['scale']); + } + if (isset($field['unique']) && $field['unique']) { + $fieldXml->addAttribute('unique', $field['unique']); + } + if (isset($field['options'])) { + $optionsXml = $fieldXml->addChild('options'); + foreach ($field['options'] as $key => $value) { + $optionsXml->addAttribute($key, $value); + } + } + if (isset($field['version'])) { + $fieldXml->addAttribute('version', $field['version']); + } + if (isset($field['columnDefinition'])) { + $fieldXml->addAttribute('column-definition', $field['columnDefinition']); + } + } + } + + foreach ($metadata->associationMappings as $name => $associationMapping) { + if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_ONE) { + $associationMappingXml = $root->addChild('one-to-one'); + } else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_ONE) { + $associationMappingXml = $root->addChild('many-to-one'); + } else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { + $associationMappingXml = $root->addChild('one-to-many'); + } else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) { + $associationMappingXml = $root->addChild('many-to-many'); + } + + $associationMappingXml->addAttribute('field', $associationMapping['fieldName']); + $associationMappingXml->addAttribute('target-entity', $associationMapping['targetEntity']); + + if (isset($associationMapping['mappedBy'])) { + $associationMappingXml->addAttribute('mapped-by', $associationMapping['mappedBy']); + } + if (isset($associationMapping['inversedBy'])) { + $associationMappingXml->addAttribute('inversed-by', $associationMapping['inversedBy']); + } + if (isset($associationMapping['orphanRemoval'])) { + $associationMappingXml->addAttribute('orphan-removal', $associationMapping['orphanRemoval']); + } + if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) { + $joinTableXml = $associationMappingXml->addChild('join-table'); + $joinTableXml->addAttribute('name', $associationMapping['joinTable']['name']); + $joinColumnsXml = $joinTableXml->addChild('join-columns'); + foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) { + $joinColumnXml = $joinColumnsXml->addChild('join-column'); + $joinColumnXml->addAttribute('name', $joinColumn['name']); + $joinColumnXml->addAttribute('referenced-column-name', $joinColumn['referencedColumnName']); + if (isset($joinColumn['onDelete'])) { + $joinColumnXml->addAttribute('on-delete', $joinColumn['onDelete']); + } + } + $inverseJoinColumnsXml = $joinTableXml->addChild('inverse-join-columns'); + foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { + $inverseJoinColumnXml = $inverseJoinColumnsXml->addChild('join-column'); + $inverseJoinColumnXml->addAttribute('name', $inverseJoinColumn['name']); + $inverseJoinColumnXml->addAttribute('referenced-column-name', $inverseJoinColumn['referencedColumnName']); + if (isset($inverseJoinColumn['onDelete'])) { + $inverseJoinColumnXml->addAttribute('on-delete', $inverseJoinColumn['onDelete']); + } + if (isset($inverseJoinColumn['columnDefinition'])) { + $inverseJoinColumnXml->addAttribute('column-definition', $inverseJoinColumn['columnDefinition']); + } + if (isset($inverseJoinColumn['nullable'])) { + $inverseJoinColumnXml->addAttribute('nullable', $inverseJoinColumn['nullable']); + } + if (isset($inverseJoinColumn['orderBy'])) { + $inverseJoinColumnXml->addAttribute('order-by', $inverseJoinColumn['orderBy']); + } + } + } + if (isset($associationMapping['joinColumns'])) { + $joinColumnsXml = $associationMappingXml->addChild('join-columns'); + foreach ($associationMapping['joinColumns'] as $joinColumn) { + $joinColumnXml = $joinColumnsXml->addChild('join-column'); + $joinColumnXml->addAttribute('name', $joinColumn['name']); + $joinColumnXml->addAttribute('referenced-column-name', $joinColumn['referencedColumnName']); + if (isset($joinColumn['onDelete'])) { + $joinColumnXml->addAttribute('on-delete', $joinColumn['onDelete']); + } + if (isset($joinColumn['columnDefinition'])) { + $joinColumnXml->addAttribute('column-definition', $joinColumn['columnDefinition']); + } + if (isset($joinColumn['nullable'])) { + $joinColumnXml->addAttribute('nullable', $joinColumn['nullable']); + } + } + } + if (isset($associationMapping['orderBy'])) { + $orderByXml = $associationMappingXml->addChild('order-by'); + foreach ($associationMapping['orderBy'] as $name => $direction) { + $orderByFieldXml = $orderByXml->addChild('order-by-field'); + $orderByFieldXml->addAttribute('name', $name); + $orderByFieldXml->addAttribute('direction', $direction); + } + } + $cascade = array(); + if ($associationMapping['isCascadeRemove']) { + $cascade[] = 'cascade-remove'; + } + if ($associationMapping['isCascadePersist']) { + $cascade[] = 'cascade-persist'; + } + if ($associationMapping['isCascadeRefresh']) { + $cascade[] = 'cascade-refresh'; + } + if ($associationMapping['isCascadeMerge']) { + $cascade[] = 'cascade-merge'; + } + if ($associationMapping['isCascadeDetach']) { + $cascade[] = 'cascade-detach'; + } + if (count($cascade) === 5) { + $cascade = array('cascade-all'); + } + if ($cascade) { + $cascadeXml = $associationMappingXml->addChild('cascade'); + foreach ($cascade as $type) { + $cascadeXml->addChild($type); + } + } + } + + if (isset($metadata->lifecycleCallbacks)) { + $lifecycleCallbacksXml = $root->addChild('lifecycle-callbacks'); + foreach ($metadata->lifecycleCallbacks as $name => $methods) { + foreach ($methods as $method) { + $lifecycleCallbackXml = $lifecycleCallbacksXml->addChild('lifecycle-callback'); + $lifecycleCallbackXml->addAttribute('type', $name); + $lifecycleCallbackXml->addAttribute('method', $method); + } + } + } + + return $this->_asXml($xml); + } + + /** + * @param \SimpleXMLElement $simpleXml + * @return string $xml + */ + private function _asXml($simpleXml) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->loadXML($simpleXml->asXML()); + $dom->formatOutput = true; + + $result = $dom->saveXML(); + return $result; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php new file mode 100644 index 0000000..525f5f4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php @@ -0,0 +1,210 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * ClassMetadata exporter for Doctrine YAML mapping files + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Jonathan Wage + */ +class YamlExporter extends AbstractExporter +{ + protected $_extension = '.dcm.yml'; + + /** + * Converts a single ClassMetadata instance to the exported format + * and returns it + * + * TODO: Should this code be pulled out in to a toArray() method in ClassMetadata + * + * @param ClassMetadataInfo $metadata + * @return mixed $exported + */ + public function exportClassMetadata(ClassMetadataInfo $metadata) + { + $array = array(); + + if ($metadata->isMappedSuperclass) { + $array['type'] = 'mappedSuperclass'; + } else { + $array['type'] = 'entity'; + } + + $array['table'] = $metadata->table['name']; + + if (isset($metadata->table['schema'])) { + $array['schema'] = $metadata->table['schema']; + } + + $inheritanceType = $metadata->inheritanceType; + if ($inheritanceType !== ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $array['inheritanceType'] = $this->_getInheritanceTypeString($inheritanceType); + } + + if ($column = $metadata->discriminatorColumn) { + $array['discriminatorColumn'] = $column; + } + + if ($map = $metadata->discriminatorMap) { + $array['discriminatorMap'] = $map; + } + + if ($metadata->changeTrackingPolicy !== ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT) { + $array['changeTrackingPolicy'] = $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy); + } + + if (isset($metadata->table['indexes'])) { + $array['indexes'] = $metadata->table['indexes']; + } + + if ($metadata->customRepositoryClassName) { + $array['repositoryClass'] = $metadata->customRepositoryClassName; + } + + if (isset($metadata->table['uniqueConstraints'])) { + $array['uniqueConstraints'] = $metadata->table['uniqueConstraints']; + } + + $fieldMappings = $metadata->fieldMappings; + + $ids = array(); + foreach ($fieldMappings as $name => $fieldMapping) { + $fieldMapping['column'] = $fieldMapping['columnName']; + unset( + $fieldMapping['columnName'], + $fieldMapping['fieldName'] + ); + + if ($fieldMapping['column'] == $name) { + unset($fieldMapping['column']); + } + + if (isset($fieldMapping['id']) && $fieldMapping['id']) { + $ids[$name] = $fieldMapping; + unset($fieldMappings[$name]); + continue; + } + + $fieldMappings[$name] = $fieldMapping; + } + + if ( ! $metadata->isIdentifierComposite && $idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $ids[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType; + } + + if ($ids) { + $array['fields'] = $ids; + } + + if ($fieldMappings) { + if ( ! isset($array['fields'])) { + $array['fields'] = array(); + } + $array['fields'] = array_merge($array['fields'], $fieldMappings); + } + + $associations = array(); + foreach ($metadata->associationMappings as $name => $associationMapping) { + $cascade = array(); + if ($associationMapping['isCascadeRemove']) { + $cascade[] = 'remove'; + } + if ($associationMapping['isCascadePersist']) { + $cascade[] = 'persist'; + } + if ($associationMapping['isCascadeRefresh']) { + $cascade[] = 'refresh'; + } + if ($associationMapping['isCascadeMerge']) { + $cascade[] = 'merge'; + } + if ($associationMapping['isCascadeDetach']) { + $cascade[] = 'detach'; + } + if (count($cascade) === 5) { + $cascade = array('all'); + } + $associationMappingArray = array( + 'targetEntity' => $associationMapping['targetEntity'], + 'cascade' => $cascade, + ); + + if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + $joinColumns = $associationMapping['joinColumns']; + $newJoinColumns = array(); + foreach ($joinColumns as $joinColumn) { + $newJoinColumns[$joinColumn['name']]['referencedColumnName'] = $joinColumn['referencedColumnName']; + if (isset($joinColumn['onDelete'])) { + $newJoinColumns[$joinColumn['name']]['onDelete'] = $joinColumn['onDelete']; + } + } + $oneToOneMappingArray = array( + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'joinColumns' => $newJoinColumns, + 'orphanRemoval' => $associationMapping['orphanRemoval'], + ); + + $associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray); + + if ($associationMapping['type'] & ClassMetadataInfo::ONE_TO_ONE) { + $array['oneToOne'][$name] = $associationMappingArray; + } else { + $array['manyToOne'][$name] = $associationMappingArray; + } + + } else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { + $oneToManyMappingArray = array( + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'orphanRemoval' => $associationMapping['orphanRemoval'], + 'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null + ); + + $associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray); + $array['oneToMany'][$name] = $associationMappingArray; + } else if ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) { + $manyToManyMappingArray = array( + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'joinTable' => isset($associationMapping['joinTable']) ? $associationMapping['joinTable'] : null, + 'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null + ); + + $associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray); + $array['manyToMany'][$name] = $associationMappingArray; + } + } + if (isset($metadata->lifecycleCallbacks)) { + $array['lifecycleCallbacks'] = $metadata->lifecycleCallbacks; + } + + return \Symfony\Component\Yaml\Yaml::dump(array($metadata->name => $array), 10); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ExportException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ExportException.php new file mode 100644 index 0000000..5ba8bd2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ExportException.php @@ -0,0 +1,23 @@ + + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ +class CountWalker extends TreeWalkerAdapter +{ + /** + * Distinct mode hint name + */ + const HINT_DISTINCT = 'doctrine_paginator.distinct'; + + /** + * Walks down a SelectStatement AST node, modifying it to retrieve a COUNT + * + * @param SelectStatement $AST + * @return void + */ + public function walkSelectStatement(SelectStatement $AST) + { + $rootComponents = array(); + foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) { + $isParent = array_key_exists('parent', $qComp) + && $qComp['parent'] === null + && $qComp['nestingLevel'] == 0 + ; + if ($isParent) { + $rootComponents[] = array($dqlAlias => $qComp); + } + } + if (count($rootComponents) > 1) { + throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); + } + $root = reset($rootComponents); + $parentName = key($root); + $parent = current($root); + + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName, + $parent['metadata']->getSingleIdentifierFieldName() + ); + $pathExpression->type = PathExpression::TYPE_STATE_FIELD; + + $distinct = $this->_getQuery()->getHint(self::HINT_DISTINCT); + $AST->selectClause->selectExpressions = array( + new SelectExpression( + new AggregateExpression('count', $pathExpression, $distinct), null + ) + ); + + // ORDER BY is not needed, only increases query execution through unnecessary sorting. + $AST->orderByClause = null; + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php new file mode 100644 index 0000000..97ed5ba --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php @@ -0,0 +1,115 @@ + + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ + +namespace Doctrine\ORM\Tools\Pagination; + +use Doctrine\DBAL\Types\Type, + Doctrine\ORM\Query\TreeWalkerAdapter, + Doctrine\ORM\Query\AST\SelectStatement, + Doctrine\ORM\Query\AST\SelectExpression, + Doctrine\ORM\Query\AST\PathExpression, + Doctrine\ORM\Query\AST\AggregateExpression; + +/** + * Replaces the selectClause of the AST with a SELECT DISTINCT root.id equivalent + * + * @category DoctrineExtensions + * @package DoctrineExtensions\Paginate + * @author David Abdemoulaie + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ +class LimitSubqueryWalker extends TreeWalkerAdapter +{ + /** + * ID type hint + */ + const IDENTIFIER_TYPE = 'doctrine_paginator.id.type'; + + /** + * @var int Counter for generating unique order column aliases + */ + private $_aliasCounter = 0; + + /** + * Walks down a SelectStatement AST node, modifying it to retrieve DISTINCT ids + * of the root Entity + * + * @param SelectStatement $AST + * @return void + */ + public function walkSelectStatement(SelectStatement $AST) + { + $parent = null; + $parentName = null; + $selectExpressions = array(); + + foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) { + // preserve mixed data in query for ordering + if (isset($qComp['resultVariable'])) { + $selectExpressions[] = new SelectExpression($qComp['resultVariable'], $dqlAlias); + continue; + } + + if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) { + $parent = $qComp; + $parentName = $dqlAlias; + continue; + } + } + + $identifier = $parent['metadata']->getSingleIdentifierFieldName(); + $this->_getQuery()->setHint( + self::IDENTIFIER_TYPE, + Type::getType($parent['metadata']->getTypeOfField($identifier)) + ); + + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, + $parentName, + $identifier + ); + $pathExpression->type = PathExpression::TYPE_STATE_FIELD; + + array_unshift($selectExpressions, new SelectExpression($pathExpression, '_dctrn_id')); + $AST->selectClause->selectExpressions = $selectExpressions; + + if (isset($AST->orderByClause)) { + foreach ($AST->orderByClause->orderByItems as $item) { + if ($item->expression instanceof PathExpression) { + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, + $item->expression->identificationVariable, + $item->expression->field + ); + $pathExpression->type = PathExpression::TYPE_STATE_FIELD; + $AST->selectClause->selectExpressions[] = new SelectExpression( + $pathExpression, + '_dctrn_ord' . $this->_aliasCounter++ + ); + } + } + } + + $AST->selectClause->isDistinct = true; + } + +} + + + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php new file mode 100644 index 0000000..760d7de --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php @@ -0,0 +1,180 @@ +. + */ + +namespace Doctrine\ORM\Tools\Pagination; + +use Doctrine\ORM\QueryBuilder; +use Doctrine\ORM\Query; +use Doctrine\ORM\NoResultException; +use Doctrine\ORM\Tools\Pagination\WhereInWalker; +use Doctrine\ORM\Tools\Pagination\CountWalker; +use Countable; +use IteratorAggregate; +use ArrayIterator; + +/** + * Paginator + * + * The paginator can handle various complex scenarios with DQL. + * + * @author Pablo Díez + * @author Benjamin Eberlei + * @license New BSD + */ +class Paginator implements \Countable, \IteratorAggregate +{ + /** + * @var Query + */ + private $query; + + /** + * @var bool + */ + private $fetchJoinCollection; + + /** + * @var int + */ + private $count; + + /** + * Constructor. + * + * @param Query|QueryBuilder $query A Doctrine ORM query or query builder. + * @param Boolean $fetchJoinCollection Whether the query joins a collection (true by default). + */ + public function __construct($query, $fetchJoinCollection = true) + { + if ($query instanceof QueryBuilder) { + $query = $query->getQuery(); + } + + $this->query = $query; + $this->fetchJoinCollection = (Boolean) $fetchJoinCollection; + } + + /** + * Returns the query + * + * @return Query + */ + public function getQuery() + { + return $this->query; + } + + /** + * Returns whether the query joins a collection. + * + * @return Boolean Whether the query joins a collection. + */ + public function getFetchJoinCollection() + { + return $this->fetchJoinCollection; + } + + /** + * {@inheritdoc} + */ + public function count() + { + if ($this->count === null) { + /* @var $countQuery Query */ + $countQuery = $this->cloneQuery($this->query); + + if ( ! $countQuery->getHint(CountWalker::HINT_DISTINCT)) { + $countQuery->setHint(CountWalker::HINT_DISTINCT, true); + } + + $countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker')); + $countQuery->setFirstResult(null)->setMaxResults(null); + + try { + $data = $countQuery->getScalarResult(); + $data = array_map('current', $data); + $this->count = array_sum($data); + } catch(NoResultException $e) { + $this->count = 0; + } + } + return $this->count; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $offset = $this->query->getFirstResult(); + $length = $this->query->getMaxResults(); + + if ($this->fetchJoinCollection) { + $subQuery = $this->cloneQuery($this->query); + $subQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker')) + ->setFirstResult($offset) + ->setMaxResults($length); + + $ids = array_map('current', $subQuery->getScalarResult()); + + $whereInQuery = $this->cloneQuery($this->query); + // don't do this for an empty id array + if (count($ids) > 0) { + $namespace = WhereInWalker::PAGINATOR_ID_ALIAS; + + $whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker')); + $whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, count($ids)); + $whereInQuery->setFirstResult(null)->setMaxResults(null); + foreach ($ids as $i => $id) { + $i++; + $whereInQuery->setParameter("{$namespace}_{$i}", $id); + } + } + + $result = $whereInQuery->getResult($this->query->getHydrationMode()); + } else { + $result = $this->cloneQuery($this->query) + ->setMaxResults($length) + ->setFirstResult($offset) + ->getResult($this->query->getHydrationMode()) + ; + } + return new \ArrayIterator($result); + } + + /** + * Clones a query. + * + * @param Query $query The query. + * + * @return Query The cloned query. + */ + private function cloneQuery(Query $query) + { + /* @var $cloneQuery Query */ + $cloneQuery = clone $query; + $cloneQuery->setParameters($query->getParameters()); + foreach ($query->getHints() as $name => $value) { + $cloneQuery->setHint($name, $value); + } + + return $cloneQuery; + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php new file mode 100644 index 0000000..5400ef2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php @@ -0,0 +1,142 @@ + + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ + +namespace Doctrine\ORM\Tools\Pagination; + +use Doctrine\ORM\Query\AST\ArithmeticExpression, + Doctrine\ORM\Query\AST\SimpleArithmeticExpression, + Doctrine\ORM\Query\TreeWalkerAdapter, + Doctrine\ORM\Query\AST\SelectStatement, + Doctrine\ORM\Query\AST\PathExpression, + Doctrine\ORM\Query\AST\InExpression, + Doctrine\ORM\Query\AST\NullComparisonExpression, + Doctrine\ORM\Query\AST\InputParameter, + Doctrine\ORM\Query\AST\ConditionalPrimary, + Doctrine\ORM\Query\AST\ConditionalTerm, + Doctrine\ORM\Query\AST\ConditionalExpression, + Doctrine\ORM\Query\AST\ConditionalFactor, + Doctrine\ORM\Query\AST\WhereClause; + +/** + * Replaces the whereClause of the AST with a WHERE id IN (:foo_1, :foo_2) equivalent + * + * @category DoctrineExtensions + * @package DoctrineExtensions\Paginate + * @author David Abdemoulaie + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ +class WhereInWalker extends TreeWalkerAdapter +{ + /** + * ID Count hint name + */ + const HINT_PAGINATOR_ID_COUNT = 'doctrine.id.count'; + + /** + * Primary key alias for query + */ + const PAGINATOR_ID_ALIAS = 'dpid'; + + /** + * Replaces the whereClause in the AST + * + * Generates a clause equivalent to WHERE IN (:dpid_1, :dpid_2, ...) + * + * The parameter namespace (dpid) is defined by + * the PAGINATOR_ID_ALIAS + * + * The total number of parameters is retrieved from + * the HINT_PAGINATOR_ID_COUNT query hint + * + * @param SelectStatement $AST + * @return void + */ + public function walkSelectStatement(SelectStatement $AST) + { + $rootComponents = array(); + foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) { + $isParent = array_key_exists('parent', $qComp) + && $qComp['parent'] === null + && $qComp['nestingLevel'] == 0 + ; + if ($isParent) { + $rootComponents[] = array($dqlAlias => $qComp); + } + } + if (count($rootComponents) > 1) { + throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); + } + $root = reset($rootComponents); + $parentName = key($root); + $parent = current($root); + + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD, $parentName, $parent['metadata']->getSingleIdentifierFieldName() + ); + $pathExpression->type = PathExpression::TYPE_STATE_FIELD; + + $count = $this->_getQuery()->getHint(self::HINT_PAGINATOR_ID_COUNT); + + if ($count > 0) { + $arithmeticExpression = new ArithmeticExpression(); + $arithmeticExpression->simpleArithmeticExpression = new SimpleArithmeticExpression( + array($pathExpression) + ); + $expression = new InExpression($arithmeticExpression); + $ns = self::PAGINATOR_ID_ALIAS; + + for ($i = 1; $i <= $count; $i++) { + $expression->literals[] = new InputParameter(":{$ns}_$i"); + } + } else { + $expression = new NullComparisonExpression($pathExpression); + $expression->not = false; + } + + $conditionalPrimary = new ConditionalPrimary; + $conditionalPrimary->simpleConditionalExpression = $expression; + if ($AST->whereClause) { + if ($AST->whereClause->conditionalExpression instanceof ConditionalTerm) { + $AST->whereClause->conditionalExpression->conditionalFactors[] = $conditionalPrimary; + } elseif ($AST->whereClause->conditionalExpression instanceof ConditionalPrimary) { + $AST->whereClause->conditionalExpression = new ConditionalExpression(array( + new ConditionalTerm(array( + $AST->whereClause->conditionalExpression, + $conditionalPrimary + )) + )); + } elseif ($AST->whereClause->conditionalExpression instanceof ConditionalExpression) { + $tmpPrimary = new ConditionalPrimary; + $tmpPrimary->conditionalExpression = $AST->whereClause->conditionalExpression; + $AST->whereClause->conditionalExpression = new ConditionalTerm(array( + $tmpPrimary, + $conditionalPrimary + )); + } + } else { + $AST->whereClause = new WhereClause( + new ConditionalExpression(array( + new ConditionalTerm(array( + $conditionalPrimary + )) + )) + ); + } + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php new file mode 100644 index 0000000..621214f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php @@ -0,0 +1,94 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Event\LoadClassMetadataEventArgs; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * ResolveTargetEntityListener + * + * Mechanism to overwrite interfaces or classes specified as association + * targets. + * + * @author Benjamin Eberlei + * @since 2.2 + */ +class ResolveTargetEntityListener +{ + /** + * @var array + */ + private $resolveTargetEntities = array(); + + /** + * Add a target-entity class name to resolve to a new class name. + * + * @param string $originalEntity + * @param string $newEntity + * @param array $mapping + * @return void + */ + public function addResolveTargetEntity($originalEntity, $newEntity, array $mapping) + { + $mapping['targetEntity'] = ltrim($newEntity, "\\"); + $this->resolveTargetEntities[ltrim($originalEntity, "\\")] = $mapping; + } + + /** + * Process event and resolve new target entity names. + * + * @param LoadClassMetadataEventArgs $args + * @return void + */ + public function loadClassMetadata(LoadClassMetadataEventArgs $args) + { + $cm = $args->getClassMetadata(); + foreach ($cm->associationMappings as $assocName => $mapping) { + if (isset($this->resolveTargetEntities[$mapping['targetEntity']])) { + $this->remapAssociation($cm, $mapping); + } + } + } + + private function remapAssociation($classMetadata, $mapping) + { + $newMapping = $this->resolveTargetEntities[$mapping['targetEntity']]; + $newMapping = array_replace_recursive($mapping, $newMapping); + $newMapping['fieldName'] = $mapping['fieldName']; + unset($classMetadata->associationMappings[$mapping['fieldName']]); + + switch ($mapping['type']) { + case ClassMetadata::MANY_TO_MANY: + $classMetadata->mapManyToMany($newMapping); + break; + case ClassMetadata::MANY_TO_ONE: + $classMetadata->mapManyToOne($newMapping); + break; + case ClassMetadata::ONE_TO_MANY: + $classMetadata->mapOneToMany($newMapping); + break; + case ClassMetadata::ONE_TO_ONE: + $classMetadata->mapOneToOne($newMapping); + break; + } + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaTool.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaTool.php new file mode 100644 index 0000000..efe7f7b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -0,0 +1,701 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\ORMException, + Doctrine\DBAL\Types\Type, + Doctrine\DBAL\Schema\Schema, + Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets, + Doctrine\ORM\EntityManager, + Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\Internal\CommitOrderCalculator, + Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs, + Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; + +/** + * The SchemaTool is a tool to create/drop/update database schemas based on + * ClassMetadata class descriptors. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class SchemaTool +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $_em; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * Initializes a new SchemaTool instance that uses the connection of the + * provided EntityManager. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->_em = $em; + $this->_platform = $em->getConnection()->getDatabasePlatform(); + } + + /** + * Creates the database schema for the given array of ClassMetadata instances. + * + * @throws ToolsException + * @param array $classes + * @return void + */ + public function createSchema(array $classes) + { + $createSchemaSql = $this->getCreateSchemaSql($classes); + $conn = $this->_em->getConnection(); + + foreach ($createSchemaSql as $sql) { + try { + $conn->executeQuery($sql); + } catch(\Exception $e) { + throw ToolsException::schemaToolFailure($sql, $e); + } + } + } + + /** + * Gets the list of DDL statements that are required to create the database schema for + * the given list of ClassMetadata instances. + * + * @param array $classes + * @return array $sql The SQL statements needed to create the schema for the classes. + */ + public function getCreateSchemaSql(array $classes) + { + $schema = $this->getSchemaFromMetadata($classes); + return $schema->toSql($this->_platform); + } + + /** + * Some instances of ClassMetadata don't need to be processed in the SchemaTool context. This method detects them. + * + * @param ClassMetadata $class + * @param array $processedClasses + * @return bool + */ + private function processingNotRequired($class, array $processedClasses) + { + return ( + isset($processedClasses[$class->name]) || + $class->isMappedSuperclass || + ($class->isInheritanceTypeSingleTable() && $class->name != $class->rootEntityName) + ); + } + + /** + * From a given set of metadata classes this method creates a Schema instance. + * + * @param array $classes + * @return Schema + */ + public function getSchemaFromMetadata(array $classes) + { + $processedClasses = array(); // Reminder for processed classes, used for hierarchies + + $sm = $this->_em->getConnection()->getSchemaManager(); + $metadataSchemaConfig = $sm->createSchemaConfig(); + $metadataSchemaConfig->setExplicitForeignKeyIndexes(false); + $schema = new Schema(array(), array(), $metadataSchemaConfig); + + $evm = $this->_em->getEventManager(); + + foreach ($classes as $class) { + if ($this->processingNotRequired($class, $processedClasses)) { + continue; + } + + $table = $schema->createTable($class->getQuotedTableName($this->_platform)); + + $columns = array(); // table columns + + if ($class->isInheritanceTypeSingleTable()) { + $columns = $this->_gatherColumns($class, $table); + $this->_gatherRelationsSql($class, $table, $schema); + + // Add the discriminator column + $this->addDiscriminatorColumnDefinition($class, $table); + + // Aggregate all the information from all classes in the hierarchy + foreach ($class->parentClasses as $parentClassName) { + // Parent class information is already contained in this class + $processedClasses[$parentClassName] = true; + } + + foreach ($class->subClasses as $subClassName) { + $subClass = $this->_em->getClassMetadata($subClassName); + $this->_gatherColumns($subClass, $table); + $this->_gatherRelationsSql($subClass, $table, $schema); + $processedClasses[$subClassName] = true; + } + } else if ($class->isInheritanceTypeJoined()) { + // Add all non-inherited fields as columns + $pkColumns = array(); + foreach ($class->fieldMappings as $fieldName => $mapping) { + if ( ! isset($mapping['inherited'])) { + $columnName = $class->getQuotedColumnName($mapping['fieldName'], $this->_platform); + $this->_gatherColumn($class, $mapping, $table); + + if ($class->isIdentifier($fieldName)) { + $pkColumns[] = $columnName; + } + } + } + + $this->_gatherRelationsSql($class, $table, $schema); + + // Add the discriminator column only to the root table + if ($class->name == $class->rootEntityName) { + $this->addDiscriminatorColumnDefinition($class, $table); + } else { + // Add an ID FK column to child tables + /* @var \Doctrine\ORM\Mapping\ClassMetadata $class */ + $idMapping = $class->fieldMappings[$class->identifier[0]]; + $this->_gatherColumn($class, $idMapping, $table); + $columnName = $class->getQuotedColumnName($class->identifier[0], $this->_platform); + // TODO: This seems rather hackish, can we optimize it? + $table->getColumn($columnName)->setAutoincrement(false); + + $pkColumns[] = $columnName; + + // Add a FK constraint on the ID column + $table->addUnnamedForeignKeyConstraint( + $this->_em->getClassMetadata($class->rootEntityName)->getQuotedTableName($this->_platform), + array($columnName), array($columnName), array('onDelete' => 'CASCADE') + ); + } + + $table->setPrimaryKey($pkColumns); + + } else if ($class->isInheritanceTypeTablePerClass()) { + throw ORMException::notSupported(); + } else { + $this->_gatherColumns($class, $table); + $this->_gatherRelationsSql($class, $table, $schema); + } + + $pkColumns = array(); + foreach ($class->identifier AS $identifierField) { + if (isset($class->fieldMappings[$identifierField])) { + $pkColumns[] = $class->getQuotedColumnName($identifierField, $this->_platform); + } else if (isset($class->associationMappings[$identifierField])) { + /* @var $assoc \Doctrine\ORM\Mapping\OneToOne */ + $assoc = $class->associationMappings[$identifierField]; + foreach ($assoc['joinColumns'] AS $joinColumn) { + $pkColumns[] = $joinColumn['name']; + } + } + } + if (!$table->hasIndex('primary')) { + $table->setPrimaryKey($pkColumns); + } + + if (isset($class->table['indexes'])) { + foreach ($class->table['indexes'] AS $indexName => $indexData) { + $table->addIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName); + } + } + + if (isset($class->table['uniqueConstraints'])) { + foreach ($class->table['uniqueConstraints'] AS $indexName => $indexData) { + $table->addUniqueIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName); + } + } + + $processedClasses[$class->name] = true; + + if ($class->isIdGeneratorSequence() && $class->name == $class->rootEntityName) { + $seqDef = $class->sequenceGeneratorDefinition; + + if (!$schema->hasSequence($seqDef['sequenceName'])) { + $schema->createSequence( + $seqDef['sequenceName'], + $seqDef['allocationSize'], + $seqDef['initialValue'] + ); + } + } + + if ($evm->hasListeners(ToolEvents::postGenerateSchemaTable)) { + $evm->dispatchEvent(ToolEvents::postGenerateSchemaTable, new GenerateSchemaTableEventArgs($class, $schema, $table)); + } + } + + if ( ! $this->_platform->supportsSchemas() && ! $this->_platform->canEmulateSchemas() ) { + $schema->visit(new RemoveNamespacedAssets()); + } + + if ($evm->hasListeners(ToolEvents::postGenerateSchema)) { + $evm->dispatchEvent(ToolEvents::postGenerateSchema, new GenerateSchemaEventArgs($this->_em, $schema)); + } + + return $schema; + } + + /** + * Gets a portable column definition as required by the DBAL for the discriminator + * column of a class. + * + * @param ClassMetadata $class + * @return array The portable column definition of the discriminator column as required by + * the DBAL. + */ + private function addDiscriminatorColumnDefinition($class, $table) + { + $discrColumn = $class->discriminatorColumn; + + if (!isset($discrColumn['type']) || (strtolower($discrColumn['type']) == 'string' && $discrColumn['length'] === null)) { + $discrColumn['type'] = 'string'; + $discrColumn['length'] = 255; + } + + $table->addColumn( + $discrColumn['name'], + $discrColumn['type'], + array('length' => $discrColumn['length'], 'notnull' => true) + ); + } + + /** + * Gathers the column definitions as required by the DBAL of all field mappings + * found in the given class. + * + * @param ClassMetadata $class + * @param Table $table + * @return array The list of portable column definitions as required by the DBAL. + */ + private function _gatherColumns($class, $table) + { + $columns = array(); + $pkColumns = array(); + + foreach ($class->fieldMappings as $fieldName => $mapping) { + if ($class->isInheritanceTypeSingleTable() && isset($mapping['inherited'])) { + continue; + } + + $column = $this->_gatherColumn($class, $mapping, $table); + + if ($class->isIdentifier($mapping['fieldName'])) { + $pkColumns[] = $class->getQuotedColumnName($mapping['fieldName'], $this->_platform); + } + } + + // For now, this is a hack required for single table inheritence, since this method is called + // twice by single table inheritence relations + if(!$table->hasIndex('primary')) { + //$table->setPrimaryKey($pkColumns); + } + + return $columns; + } + + /** + * Creates a column definition as required by the DBAL from an ORM field mapping definition. + * + * @param ClassMetadata $class The class that owns the field mapping. + * @param array $mapping The field mapping. + * @param Table $table + * @return array The portable column definition as required by the DBAL. + */ + private function _gatherColumn($class, array $mapping, $table) + { + $columnName = $class->getQuotedColumnName($mapping['fieldName'], $this->_platform); + $columnType = $mapping['type']; + + $options = array(); + $options['length'] = isset($mapping['length']) ? $mapping['length'] : null; + $options['notnull'] = isset($mapping['nullable']) ? ! $mapping['nullable'] : true; + if ($class->isInheritanceTypeSingleTable() && count($class->parentClasses) > 0) { + $options['notnull'] = false; + } + + $options['platformOptions'] = array(); + $options['platformOptions']['version'] = $class->isVersioned && $class->versionField == $mapping['fieldName'] ? true : false; + + if(strtolower($columnType) == 'string' && $options['length'] === null) { + $options['length'] = 255; + } + + if (isset($mapping['precision'])) { + $options['precision'] = $mapping['precision']; + } + + if (isset($mapping['scale'])) { + $options['scale'] = $mapping['scale']; + } + + if (isset($mapping['default'])) { + $options['default'] = $mapping['default']; + } + + if (isset($mapping['columnDefinition'])) { + $options['columnDefinition'] = $mapping['columnDefinition']; + } + + if (isset($mapping['options'])) { + $options['customSchemaOptions'] = $mapping['options']; + } + + if ($class->isIdGeneratorIdentity() && $class->getIdentifierFieldNames() == array($mapping['fieldName'])) { + $options['autoincrement'] = true; + } + if ($class->isInheritanceTypeJoined() && $class->name != $class->rootEntityName) { + $options['autoincrement'] = false; + } + + if ($table->hasColumn($columnName)) { + // required in some inheritance scenarios + $table->changeColumn($columnName, $options); + } else { + $table->addColumn($columnName, $columnType, $options); + } + + $isUnique = isset($mapping['unique']) ? $mapping['unique'] : false; + if ($isUnique) { + $table->addUniqueIndex(array($columnName)); + } + } + + /** + * Gathers the SQL for properly setting up the relations of the given class. + * This includes the SQL for foreign key constraints and join tables. + * + * @param ClassMetadata $class + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\Schema $schema + * @return void + */ + private function _gatherRelationsSql($class, $table, $schema) + { + foreach ($class->associationMappings as $fieldName => $mapping) { + if (isset($mapping['inherited'])) { + continue; + } + + $foreignClass = $this->_em->getClassMetadata($mapping['targetEntity']); + + if ($mapping['type'] & ClassMetadata::TO_ONE && $mapping['isOwningSide']) { + $primaryKeyColumns = $uniqueConstraints = array(); // PK is unnecessary for this relation-type + + $this->_gatherRelationJoinColumns($mapping['joinColumns'], $table, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints); + + foreach($uniqueConstraints AS $indexName => $unique) { + $table->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName); + } + } else if ($mapping['type'] == ClassMetadata::ONE_TO_MANY && $mapping['isOwningSide']) { + //... create join table, one-many through join table supported later + throw ORMException::notSupported(); + } else if ($mapping['type'] == ClassMetadata::MANY_TO_MANY && $mapping['isOwningSide']) { + // create join table + $joinTable = $mapping['joinTable']; + + $theJoinTable = $schema->createTable($foreignClass->getQuotedJoinTableName($mapping, $this->_platform)); + + $primaryKeyColumns = $uniqueConstraints = array(); + + // Build first FK constraint (relation table => source table) + $this->_gatherRelationJoinColumns($joinTable['joinColumns'], $theJoinTable, $class, $mapping, $primaryKeyColumns, $uniqueConstraints); + + // Build second FK constraint (relation table => target table) + $this->_gatherRelationJoinColumns($joinTable['inverseJoinColumns'], $theJoinTable, $foreignClass, $mapping, $primaryKeyColumns, $uniqueConstraints); + + $theJoinTable->setPrimaryKey($primaryKeyColumns); + + foreach($uniqueConstraints AS $indexName => $unique) { + $theJoinTable->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName); + } + } + } + } + + /** + * Get the class metadata that is responsible for the definition of the referenced column name. + * + * Previously this was a simple task, but with DDC-117 this problem is actually recursive. If its + * not a simple field, go through all identifier field names that are associations recursivly and + * find that referenced column name. + * + * TODO: Is there any way to make this code more pleasing? + * + * @param ClassMetadata $class + * @param string $referencedColumnName + * @return array(ClassMetadata, referencedFieldName) + */ + private function getDefiningClass($class, $referencedColumnName) + { + $referencedFieldName = $class->getFieldName($referencedColumnName); + + if ($class->hasField($referencedFieldName)) { + return array($class, $referencedFieldName); + } else if (in_array($referencedColumnName, $class->getIdentifierColumnNames())) { + // it seems to be an entity as foreign key + foreach ($class->getIdentifierFieldNames() AS $fieldName) { + if ($class->hasAssociation($fieldName) && $class->getSingleAssociationJoinColumnName($fieldName) == $referencedColumnName) { + return $this->getDefiningClass( + $this->_em->getClassMetadata($class->associationMappings[$fieldName]['targetEntity']), + $class->getSingleAssociationReferencedJoinColumnName($fieldName) + ); + } + } + } + + return null; + } + + /** + * Gather columns and fk constraints that are required for one part of relationship. + * + * @param array $joinColumns + * @param \Doctrine\DBAL\Schema\Table $theJoinTable + * @param ClassMetadata $class + * @param array $mapping + * @param array $primaryKeyColumns + * @param array $uniqueConstraints + */ + private function _gatherRelationJoinColumns($joinColumns, $theJoinTable, $class, $mapping, &$primaryKeyColumns, &$uniqueConstraints) + { + $localColumns = array(); + $foreignColumns = array(); + $fkOptions = array(); + $foreignTableName = $class->getQuotedTableName($this->_platform); + + foreach ($joinColumns as $joinColumn) { + $columnName = $joinColumn['name']; + list($definingClass, $referencedFieldName) = $this->getDefiningClass($class, $joinColumn['referencedColumnName']); + + if (!$definingClass) { + throw new \Doctrine\ORM\ORMException( + "Column name `".$joinColumn['referencedColumnName']."` referenced for relation from ". + $mapping['sourceEntity'] . " towards ". $mapping['targetEntity'] . " does not exist." + ); + } + + $primaryKeyColumns[] = $columnName; + $localColumns[] = $columnName; + $foreignColumns[] = $joinColumn['referencedColumnName']; + + if ( ! $theJoinTable->hasColumn($joinColumn['name'])) { + // Only add the column to the table if it does not exist already. + // It might exist already if the foreign key is mapped into a regular + // property as well. + + $fieldMapping = $definingClass->getFieldMapping($referencedFieldName); + + $columnDef = null; + if (isset($joinColumn['columnDefinition'])) { + $columnDef = $joinColumn['columnDefinition']; + } else if (isset($fieldMapping['columnDefinition'])) { + $columnDef = $fieldMapping['columnDefinition']; + } + $columnOptions = array('notnull' => false, 'columnDefinition' => $columnDef); + if (isset($joinColumn['nullable'])) { + $columnOptions['notnull'] = !$joinColumn['nullable']; + } + if ($fieldMapping['type'] == "string" && isset($fieldMapping['length'])) { + $columnOptions['length'] = $fieldMapping['length']; + } else if ($fieldMapping['type'] == "decimal") { + $columnOptions['scale'] = $fieldMapping['scale']; + $columnOptions['precision'] = $fieldMapping['precision']; + } + + $theJoinTable->addColumn($columnName, $fieldMapping['type'], $columnOptions); + } + + if (isset($joinColumn['unique']) && $joinColumn['unique'] == true) { + $uniqueConstraints[] = array('columns' => array($columnName)); + } + + if (isset($joinColumn['onDelete'])) { + $fkOptions['onDelete'] = $joinColumn['onDelete']; + } + } + + $theJoinTable->addUnnamedForeignKeyConstraint( + $foreignTableName, $localColumns, $foreignColumns, $fkOptions + ); + } + + /** + * Drops the database schema for the given classes. + * + * In any way when an exception is thrown it is supressed since drop was + * issued for all classes of the schema and some probably just don't exist. + * + * @param array $classes + * @return void + */ + public function dropSchema(array $classes) + { + $dropSchemaSql = $this->getDropSchemaSQL($classes); + $conn = $this->_em->getConnection(); + + foreach ($dropSchemaSql as $sql) { + try { + $conn->executeQuery($sql); + } catch(\Exception $e) { + + } + } + } + + /** + * Drops all elements in the database of the current connection. + * + * @return void + */ + public function dropDatabase() + { + $dropSchemaSql = $this->getDropDatabaseSQL(); + $conn = $this->_em->getConnection(); + + foreach ($dropSchemaSql as $sql) { + $conn->executeQuery($sql); + } + } + + /** + * Gets the SQL needed to drop the database schema for the connections database. + * + * @return array + */ + public function getDropDatabaseSQL() + { + $sm = $this->_em->getConnection()->getSchemaManager(); + $schema = $sm->createSchema(); + + $visitor = new \Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector($this->_platform); + /* @var $schema \Doctrine\DBAL\Schema\Schema */ + $schema->visit($visitor); + return $visitor->getQueries(); + } + + /** + * Get SQL to drop the tables defined by the passed classes. + * + * @param array $classes + * @return array + */ + public function getDropSchemaSQL(array $classes) + { + $visitor = new \Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector($this->_platform); + $schema = $this->getSchemaFromMetadata($classes); + + $sm = $this->_em->getConnection()->getSchemaManager(); + $fullSchema = $sm->createSchema(); + foreach ($fullSchema->getTables() AS $table) { + if (!$schema->hasTable($table->getName())) { + foreach ($table->getForeignKeys() AS $foreignKey) { + /* @var $foreignKey \Doctrine\DBAL\Schema\ForeignKeyConstraint */ + if ($schema->hasTable($foreignKey->getForeignTableName())) { + $visitor->acceptForeignKey($table, $foreignKey); + } + } + } else { + $visitor->acceptTable($table); + foreach ($table->getForeignKeys() AS $foreignKey) { + $visitor->acceptForeignKey($table, $foreignKey); + } + } + } + + if ($this->_platform->supportsSequences()) { + foreach ($schema->getSequences() AS $sequence) { + $visitor->acceptSequence($sequence); + } + foreach ($schema->getTables() AS $table) { + /* @var $sequence Table */ + if ($table->hasPrimaryKey()) { + $columns = $table->getPrimaryKey()->getColumns(); + if (count($columns) == 1) { + $checkSequence = $table->getName() . "_" . $columns[0] . "_seq"; + if ($fullSchema->hasSequence($checkSequence)) { + $visitor->acceptSequence($fullSchema->getSequence($checkSequence)); + } + } + } + } + } + + return $visitor->getQueries(); + } + + /** + * Updates the database schema of the given classes by comparing the ClassMetadata + * instances to the current database schema that is inspected. If $saveMode is set + * to true the command is executed in the Database, else SQL is returned. + * + * @param array $classes + * @param boolean $saveMode + * @return void + */ + public function updateSchema(array $classes, $saveMode=false) + { + $updateSchemaSql = $this->getUpdateSchemaSql($classes, $saveMode); + $conn = $this->_em->getConnection(); + + foreach ($updateSchemaSql as $sql) { + $conn->executeQuery($sql); + } + } + + /** + * Gets the sequence of SQL statements that need to be performed in order + * to bring the given class mappings in-synch with the relational schema. + * If $saveMode is set to true the command is executed in the Database, + * else SQL is returned. + * + * @param array $classes The classes to consider. + * @param boolean $saveMode True for writing to DB, false for SQL string + * @return array The sequence of SQL statements. + */ + public function getUpdateSchemaSql(array $classes, $saveMode=false) + { + $sm = $this->_em->getConnection()->getSchemaManager(); + + $fromSchema = $sm->createSchema(); + $toSchema = $this->getSchemaFromMetadata($classes); + + $comparator = new \Doctrine\DBAL\Schema\Comparator(); + $schemaDiff = $comparator->compare($fromSchema, $toSchema); + + if ($saveMode) { + return $schemaDiff->toSaveSql($this->_platform); + } else { + return $schemaDiff->toSql($this->_platform); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaValidator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaValidator.php new file mode 100644 index 0000000..0932f09 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaValidator.php @@ -0,0 +1,267 @@ +. +*/ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\DBAL\Types\Type; + +/** + * Performs strict validation of the mapping schema + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SchemaValidator +{ + /** + * @var EntityManager + */ + private $em; + + /** + * @param EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + } + + /** + * Checks the internal consistency of all mapping files. + * + * There are several checks that can't be done at runtime or are too expensive, which can be verified + * with this command. For example: + * + * 1. Check if a relation with "mappedBy" is actually connected to that specified field. + * 2. Check if "mappedBy" and "inversedBy" are consistent to each other. + * 3. Check if "referencedColumnName" attributes are really pointing to primary key columns. + * 4. Check if there are public properties that might cause problems with lazy loading. + * + * @return array + */ + public function validateMapping() + { + $errors = array(); + $cmf = $this->em->getMetadataFactory(); + $classes = $cmf->getAllMetadata(); + + foreach ($classes AS $class) { + if ($ce = $this->validateClass($class)) { + $errors[$class->name] = $ce; + } + } + + return $errors; + } + + /** + * Validate a single class of the current + * + * @param ClassMetadataInfo $class + * @return array + */ + public function validateClass(ClassMetadataInfo $class) + { + $ce = array(); + $cmf = $this->em->getMetadataFactory(); + + foreach ($class->fieldMappings as $fieldName => $mapping) { + if (!Type::hasType($mapping['type'])) { + $ce[] = "The field '" . $class->name . "#" . $fieldName."' uses a non-existant type '" . $mapping['type'] . "'."; + } + } + + foreach ($class->associationMappings AS $fieldName => $assoc) { + if (!class_exists($assoc['targetEntity']) || $cmf->isTransient($assoc['targetEntity'])) { + $ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is unknown or not an entity.'; + return $ce; + } + + if ($assoc['mappedBy'] && $assoc['inversedBy']) { + $ce[] = "The association " . $class . "#" . $fieldName . " cannot be defined as both inverse and owning."; + } + + $targetMetadata = $cmf->getMetadataFor($assoc['targetEntity']); + + if (isset($assoc['id']) && $targetMetadata->containsForeignIdentifier) { + $ce[] = "Cannot map association '" . $class->name. "#". $fieldName ." as identifier, because " . + "the target entity '". $targetMetadata->name . "' also maps an association as identifier."; + } + + /* @var $assoc AssociationMapping */ + if ($assoc['mappedBy']) { + if ($targetMetadata->hasField($assoc['mappedBy'])) { + $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ". + "field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which is not defined as association."; + } + if (!$targetMetadata->hasAssociation($assoc['mappedBy'])) { + $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ". + "field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which does not exist."; + } else if ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] == null) { + $ce[] = "The field " . $class->name . "#" . $fieldName . " is on the inverse side of a ". + "bi-directional relationship, but the specified mappedBy association on the target-entity ". + $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ". + "'inversedBy=".$fieldName."' attribute."; + } else if ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] != $fieldName) { + $ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " . + $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " are ". + "incosistent with each other."; + } + } + + if ($assoc['inversedBy']) { + if ($targetMetadata->hasField($assoc['inversedBy'])) { + $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ". + "field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which is not defined as association."; + } + if (!$targetMetadata->hasAssociation($assoc['inversedBy'])) { + $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ". + "field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which does not exist."; + } else if ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] == null) { + $ce[] = "The field " . $class->name . "#" . $fieldName . " is on the owning side of a ". + "bi-directional relationship, but the specified mappedBy association on the target-entity ". + $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ". + "'inversedBy' attribute."; + } else if ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] != $fieldName) { + $ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " . + $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " are ". + "incosistent with each other."; + } + + // Verify inverse side/owning side match each other + if (array_key_exists($assoc['inversedBy'], $targetMetadata->associationMappings)) { + $targetAssoc = $targetMetadata->associationMappings[$assoc['inversedBy']]; + if ($assoc['type'] == ClassMetadataInfo::ONE_TO_ONE && $targetAssoc['type'] !== ClassMetadataInfo::ONE_TO_ONE){ + $ce[] = "If association " . $class->name . "#" . $fieldName . " is one-to-one, then the inversed " . + "side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be one-to-one as well."; + } else if ($assoc['type'] == ClassMetadataInfo::MANY_TO_ONE && $targetAssoc['type'] !== ClassMetadataInfo::ONE_TO_MANY){ + $ce[] = "If association " . $class->name . "#" . $fieldName . " is many-to-one, then the inversed " . + "side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be one-to-many."; + } else if ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY && $targetAssoc['type'] !== ClassMetadataInfo::MANY_TO_MANY){ + $ce[] = "If association " . $class->name . "#" . $fieldName . " is many-to-many, then the inversed " . + "side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be many-to-many as well."; + } + } + } + + if ($assoc['isOwningSide']) { + if ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY) { + $identifierColumns = $class->getIdentifierColumnNames(); + foreach ($assoc['joinTable']['joinColumns'] AS $joinColumn) { + if (!in_array($joinColumn['referencedColumnName'], $identifierColumns)) { + $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " . + "has to be a primary key column on the target entity class '".$class->name."'."; + break; + } + } + + $identifierColumns = $targetMetadata->getIdentifierColumnNames(); + foreach ($assoc['joinTable']['inverseJoinColumns'] AS $inverseJoinColumn) { + if (!in_array($inverseJoinColumn['referencedColumnName'], $identifierColumns)) { + $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " . + "has to be a primary key column on the target entity class '".$targetMetadata->name."'."; + break; + } + } + + if (count($targetMetadata->getIdentifierColumnNames()) != count($assoc['joinTable']['inverseJoinColumns'])) { + $ce[] = "The inverse join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " . + "have to contain to ALL identifier columns of the target entity '". $targetMetadata->name . "', " . + "however '" . implode(", ", array_diff($targetMetadata->getIdentifierColumnNames(), array_values($assoc['relationToTargetKeyColumns']))) . + "' are missing."; + } + + if (count($class->getIdentifierColumnNames()) != count($assoc['joinTable']['joinColumns'])) { + $ce[] = "The join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " . + "have to contain to ALL identifier columns of the source entity '". $class->name . "', " . + "however '" . implode(", ", array_diff($class->getIdentifierColumnNames(), array_values($assoc['relationToSourceKeyColumns']))) . + "' are missing."; + } + + } else if ($assoc['type'] & ClassMetadataInfo::TO_ONE) { + $identifierColumns = $targetMetadata->getIdentifierColumnNames(); + foreach ($assoc['joinColumns'] AS $joinColumn) { + if (!in_array($joinColumn['referencedColumnName'], $identifierColumns)) { + $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " . + "has to be a primary key column on the target entity class '".$targetMetadata->name."'."; + } + } + + if (count($identifierColumns) != count($assoc['joinColumns'])) { + $ids = array(); + foreach ($assoc['joinColumns'] AS $joinColumn) { + $ids[] = $joinColumn['name']; + } + + $ce[] = "The join columns of the association '" . $assoc['fieldName'] . "' " . + "have to match to ALL identifier columns of the target entity '". $class->name . "', " . + "however '" . implode(", ", array_diff($targetMetadata->getIdentifierColumnNames(), $ids)) . + "' are missing."; + } + } + } + + if (isset($assoc['orderBy']) && $assoc['orderBy'] !== null) { + foreach ($assoc['orderBy'] AS $orderField => $orientation) { + if (!$targetMetadata->hasField($orderField)) { + $ce[] = "The association " . $class->name."#".$fieldName." is ordered by a foreign field " . + $orderField . " that is not a field on the target entity " . $targetMetadata->name; + } + } + } + } + + foreach ($class->reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $publicAttr) { + if ($publicAttr->isStatic()) { + continue; + } + $ce[] = "Field '".$publicAttr->getName()."' in class '".$class->name."' must be private ". + "or protected. Public fields may break lazy-loading."; + } + + foreach ($class->subClasses AS $subClass) { + if (!in_array($class->name, class_parents($subClass))) { + $ce[] = "According to the discriminator map class '" . $subClass . "' has to be a child ". + "of '" . $class->name . "' but these entities are not related through inheritance."; + } + } + + return $ce; + } + + /** + * Check if the Database Schema is in sync with the current metadata state. + * + * @return bool + */ + public function schemaInSyncWithMetadata() + { + $schemaTool = new SchemaTool($this->em); + + $allMetadata = $this->em->getMetadataFactory()->getAllMetadata(); + return (count($schemaTool->getUpdateSchemaSql($allMetadata, true)) == 0); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Setup.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Setup.php new file mode 100644 index 0000000..c2550e2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Setup.php @@ -0,0 +1,194 @@ +. +*/ + +namespace Doctrine\ORM\Tools; + +use Doctrine\Common\ClassLoader; +use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Cache\ArrayCache; +use Doctrine\ORM\Configuration; +use Doctrine\ORM\Mapping\Driver\XmlDriver; +use Doctrine\ORM\Mapping\Driver\YamlDriver; + +/** + * Convenience class for setting up Doctrine from different installations and configurations. + * + * @author Benjamin Eberlei + */ +class Setup +{ + /** + * Use this method to register all autoloaders for a setup where Doctrine is checked out from + * its github repository at {@link http://github.com/doctrine/doctrine2} + * + * @param string $gitCheckoutRootPath + * @return void + */ + static public function registerAutoloadGit($gitCheckoutRootPath) + { + if (!class_exists('Doctrine\Common\ClassLoader', false)) { + require_once $gitCheckoutRootPath . "/lib/vendor/doctrine-common/lib/Doctrine/Common/ClassLoader.php"; + } + + $loader = new ClassLoader("Doctrine\Common", $gitCheckoutRootPath . "/lib/vendor/doctrine-common/lib"); + $loader->register(); + + $loader = new ClassLoader("Doctrine\DBAL", $gitCheckoutRootPath . "/lib/vendor/doctrine-dbal/lib"); + $loader->register(); + + $loader = new ClassLoader("Doctrine\ORM", $gitCheckoutRootPath . "/lib"); + $loader->register(); + + $loader = new ClassLoader("Symfony\Component", $gitCheckoutRootPath . "/lib/vendor"); + $loader->register(); + } + + /** + * Use this method to register all autoloaders for a setup where Doctrine is installed + * though {@link http://pear.doctrine-project.org}. + * + * @return void + */ + static public function registerAutoloadPEAR() + { + if (!class_exists('Doctrine\Common\ClassLoader', false)) { + require_once "Doctrine/Common/ClassLoader.php"; + } + + $loader = new ClassLoader("Doctrine"); + $loader->register(); + + $parts = explode(PATH_SEPARATOR, get_include_path()); + + foreach ($parts AS $includePath) { + if ($includePath != "." && file_exists($includePath . "/Doctrine")) { + $loader = new ClassLoader("Symfony\Component", $includePath . "/Doctrine"); + $loader->register(); + return; + } + } + } + + /** + * Use this method to register all autoloads for a downloaded Doctrine library. + * Pick the directory the library was uncompressed into. + * + * @param string $directory + */ + static public function registerAutoloadDirectory($directory) + { + if (!class_exists('Doctrine\Common\ClassLoader', false)) { + require_once $directory . "/Doctrine/Common/ClassLoader.php"; + } + + $loader = new ClassLoader("Doctrine", $directory); + $loader->register(); + + $loader = new ClassLoader("Symfony\Component", $directory . "/Doctrine"); + $loader->register(); + } + + /** + * Create a configuration with an annotation metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * @return Configuration + */ + static public function createAnnotationMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null) + { + $config = self::createConfiguration($isDevMode, $proxyDir, $cache); + $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver($paths)); + return $config; + } + + /** + * Create a configuration with a xml metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * @return Configuration + */ + static public function createXMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null) + { + $config = self::createConfiguration($isDevMode, $proxyDir, $cache); + $config->setMetadataDriverImpl(new XmlDriver($paths)); + return $config; + } + + /** + * Create a configuration with a yaml metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * @return Configuration + */ + static public function createYAMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null) + { + $config = self::createConfiguration($isDevMode, $proxyDir, $cache); + $config->setMetadataDriverImpl(new YamlDriver($paths)); + return $config; + } + + /** + * Create a configuration without a metadata driver. + * + * @param bool $isDevMode + * @param string $proxyDir + * @param Cache $cache + * @return Configuration + */ + static public function createConfiguration($isDevMode = false, $proxyDir = null, Cache $cache = null) + { + $proxyDir = $proxyDir ?: sys_get_temp_dir(); + if ($isDevMode === false && $cache === null) { + if (extension_loaded('apc')) { + $cache = new \Doctrine\Common\Cache\ApcCache; + } else if (extension_loaded('xcache')) { + $cache = new \Doctrine\Common\Cache\XcacheCache; + } else if (extension_loaded('memcache')) { + $memcache = new \Memcache(); + $memcache->connect('127.0.0.1'); + $cache = new \Doctrine\Common\Cache\MemcacheCache(); + $cache->setMemcache($memcache); + } else { + $cache = new ArrayCache; + } + } else if ($cache === null) { + $cache = new ArrayCache; + } + $cache->setNamespace("dc2_" . md5($proxyDir) . "_"); // to avoid collisions + + $config = new Configuration(); + $config->setMetadataCacheImpl($cache); + $config->setQueryCacheImpl($cache); + $config->setResultCacheImpl($cache); + $config->setProxyDir( $proxyDir ); + $config->setProxyNamespace('DoctrineProxies'); + $config->setAutoGenerateProxyClasses($isDevMode); + + return $config; + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolEvents.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolEvents.php new file mode 100644 index 0000000..7ea1f57 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolEvents.php @@ -0,0 +1,44 @@ +. +*/ + +namespace Doctrine\ORM\Tools; + +class ToolEvents +{ + /** + * The postGenerateSchemaTable event occurs in SchemaTool#getSchemaFromMetadata() + * whenever an entity class is transformed into its table representation. It recieves + * the current non-complete Schema instance, the Entity Metadata Class instance and + * the Schema Table instance of this entity. + * + * @var string + */ + const postGenerateSchemaTable = 'postGenerateSchemaTable'; + + /** + * The postGenerateSchema event is triggered in SchemaTool#getSchemaFromMetadata() + * after all entity classes have been transformed into the related Schema structure. + * The EventArgs contain the EntityManager and the created Schema instance. + * + * @var string + */ + const postGenerateSchema = 'postGenerateSchema'; +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolsException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolsException.php new file mode 100644 index 0000000..4551d87 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolsException.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\ORMException; + +/** + * Tools related Exceptions + * + * @author Benjamin Eberlei + */ +class ToolsException extends ORMException +{ + public static function schemaToolFailure($sql, \Exception $e) + { + return new self("Schema-Tool failed with Error '" . $e->getMessage() . "' while executing DDL: " . $sql, "0", $e); + } + + public static function couldNotMapDoctrine1Type($type) + { + return new self("Could not map doctrine 1 type '$type'!"); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/TransactionRequiredException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/TransactionRequiredException.php new file mode 100644 index 0000000..170f63e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/TransactionRequiredException.php @@ -0,0 +1,40 @@ +. +*/ + +namespace Doctrine\ORM; + +/** + * Is thrown when a transaction is required for the current operation, but there is none open. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 1.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class TransactionRequiredException extends ORMException +{ + static public function transactionRequired() + { + return new self('An open transaction is required for this operation.'); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php b/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php new file mode 100644 index 0000000..965c7d5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php @@ -0,0 +1,2949 @@ +. + */ + +namespace Doctrine\ORM; + +use Exception, InvalidArgumentException, UnexpectedValueException, + Doctrine\Common\Collections\ArrayCollection, + Doctrine\Common\Collections\Collection, + Doctrine\Common\NotifyPropertyChanged, + Doctrine\Common\PropertyChangedListener, + Doctrine\Common\Persistence\ObjectManagerAware, + Doctrine\ORM\Event\LifecycleEventArgs, + Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\Proxy\Proxy; + +/** + * The UnitOfWork is responsible for tracking changes to objects during an + * "object-level" transaction and for writing out changes to the database + * in the correct order. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @internal This class contains highly performance-sensitive code. + */ +class UnitOfWork implements PropertyChangedListener +{ + /** + * An entity is in MANAGED state when its persistence is managed by an EntityManager. + */ + const STATE_MANAGED = 1; + + /** + * An entity is new if it has just been instantiated (i.e. using the "new" operator) + * and is not (yet) managed by an EntityManager. + */ + const STATE_NEW = 2; + + /** + * A detached entity is an instance with persistent state and identity that is not + * (or no longer) associated with an EntityManager (and a UnitOfWork). + */ + const STATE_DETACHED = 3; + + /** + * A removed entity instance is an instance with a persistent identity, + * associated with an EntityManager, whose persistent state will be deleted + * on commit. + */ + const STATE_REMOVED = 4; + + /** + * The identity map that holds references to all managed entities that have + * an identity. The entities are grouped by their class name. + * Since all classes in a hierarchy must share the same identifier set, + * we always take the root class name of the hierarchy. + * + * @var array + */ + private $identityMap = array(); + + /** + * Map of all identifiers of managed entities. + * Keys are object ids (spl_object_hash). + * + * @var array + */ + private $entityIdentifiers = array(); + + /** + * Map of the original entity data of managed entities. + * Keys are object ids (spl_object_hash). This is used for calculating changesets + * at commit time. + * + * @var array + * @internal Note that PHPs "copy-on-write" behavior helps a lot with memory usage. + * A value will only really be copied if the value in the entity is modified + * by the user. + */ + private $originalEntityData = array(); + + /** + * Map of entity changes. Keys are object ids (spl_object_hash). + * Filled at the beginning of a commit of the UnitOfWork and cleaned at the end. + * + * @var array + */ + private $entityChangeSets = array(); + + /** + * The (cached) states of any known entities. + * Keys are object ids (spl_object_hash). + * + * @var array + */ + private $entityStates = array(); + + /** + * Map of entities that are scheduled for dirty checking at commit time. + * This is only used for entities with a change tracking policy of DEFERRED_EXPLICIT. + * Keys are object ids (spl_object_hash). + * + * @var array + * @todo rename: scheduledForSynchronization + */ + private $scheduledForDirtyCheck = array(); + + /** + * A list of all pending entity insertions. + * + * @var array + */ + private $entityInsertions = array(); + + /** + * A list of all pending entity updates. + * + * @var array + */ + private $entityUpdates = array(); + + /** + * Any pending extra updates that have been scheduled by persisters. + * + * @var array + */ + private $extraUpdates = array(); + + /** + * A list of all pending entity deletions. + * + * @var array + */ + private $entityDeletions = array(); + + /** + * All pending collection deletions. + * + * @var array + */ + private $collectionDeletions = array(); + + /** + * All pending collection updates. + * + * @var array + */ + private $collectionUpdates = array(); + + /** + * List of collections visited during changeset calculation on a commit-phase of a UnitOfWork. + * At the end of the UnitOfWork all these collections will make new snapshots + * of their data. + * + * @var array + */ + private $visitedCollections = array(); + + /** + * The EntityManager that "owns" this UnitOfWork instance. + * + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * The calculator used to calculate the order in which changes to + * entities need to be written to the database. + * + * @var \Doctrine\ORM\Internal\CommitOrderCalculator + */ + private $commitOrderCalculator; + + /** + * The entity persister instances used to persist entity instances. + * + * @var array + */ + private $persisters = array(); + + /** + * The collection persister instances used to persist collections. + * + * @var array + */ + private $collectionPersisters = array(); + + /** + * The EventManager used for dispatching events. + * + * @var EventManager + */ + private $evm; + + /** + * Orphaned entities that are scheduled for removal. + * + * @var array + */ + private $orphanRemovals = array(); + + /** + * Read-Only objects are never evaluated + * + * @var array + */ + private $readOnlyObjects = array(); + + /** + * Map of Entity Class-Names and corresponding IDs that should eager loaded when requested. + * + * @var array + */ + private $eagerLoadingEntities = array(); + + /** + * Initializes a new UnitOfWork instance, bound to the given EntityManager. + * + * @param \Doctrine\ORM\EntityManager $em + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + $this->evm = $em->getEventManager(); + } + + /** + * Commits the UnitOfWork, executing all operations that have been postponed + * up to this point. The state of all managed entities will be synchronized with + * the database. + * + * The operations are executed in the following order: + * + * 1) All entity insertions + * 2) All entity updates + * 3) All collection deletions + * 4) All collection updates + * 5) All entity deletions + * + * @param object $entity + * @return void + */ + public function commit($entity = null) + { + // Raise preFlush + if ($this->evm->hasListeners(Events::preFlush)) { + $this->evm->dispatchEvent(Events::preFlush, new Event\PreFlushEventArgs($this->em)); + } + + // Compute changes done since last commit. + if ($entity === null) { + $this->computeChangeSets(); + } else { + $this->computeSingleEntityChangeSet($entity); + } + + if ( ! ($this->entityInsertions || + $this->entityDeletions || + $this->entityUpdates || + $this->collectionUpdates || + $this->collectionDeletions || + $this->orphanRemovals)) { + return; // Nothing to do. + } + + if ($this->orphanRemovals) { + foreach ($this->orphanRemovals as $orphan) { + $this->remove($orphan); + } + } + + // Raise onFlush + if ($this->evm->hasListeners(Events::onFlush)) { + $this->evm->dispatchEvent(Events::onFlush, new Event\OnFlushEventArgs($this->em)); + } + + // Now we need a commit order to maintain referential integrity + $commitOrder = $this->getCommitOrder(); + + $conn = $this->em->getConnection(); + $conn->beginTransaction(); + + try { + if ($this->entityInsertions) { + foreach ($commitOrder as $class) { + $this->executeInserts($class); + } + } + + if ($this->entityUpdates) { + foreach ($commitOrder as $class) { + $this->executeUpdates($class); + } + } + + // Extra updates that were requested by persisters. + if ($this->extraUpdates) { + $this->executeExtraUpdates(); + } + + // Collection deletions (deletions of complete collections) + foreach ($this->collectionDeletions as $collectionToDelete) { + $this->getCollectionPersister($collectionToDelete->getMapping())->delete($collectionToDelete); + } + // Collection updates (deleteRows, updateRows, insertRows) + foreach ($this->collectionUpdates as $collectionToUpdate) { + $this->getCollectionPersister($collectionToUpdate->getMapping())->update($collectionToUpdate); + } + + // Entity deletions come last and need to be in reverse commit order + if ($this->entityDeletions) { + for ($count = count($commitOrder), $i = $count - 1; $i >= 0; --$i) { + $this->executeDeletions($commitOrder[$i]); + } + } + + $conn->commit(); + } catch (Exception $e) { + $this->em->close(); + $conn->rollback(); + + throw $e; + } + + // Take new snapshots from visited collections + foreach ($this->visitedCollections as $coll) { + $coll->takeSnapshot(); + } + + // Raise postFlush + if ($this->evm->hasListeners(Events::postFlush)) { + $this->evm->dispatchEvent(Events::postFlush, new Event\PostFlushEventArgs($this->em)); + } + + // Clear up + $this->entityInsertions = + $this->entityUpdates = + $this->entityDeletions = + $this->extraUpdates = + $this->entityChangeSets = + $this->collectionUpdates = + $this->collectionDeletions = + $this->visitedCollections = + $this->scheduledForDirtyCheck = + $this->orphanRemovals = array(); + } + + /** + * Compute the changesets of all entities scheduled for insertion + * + * @return void + */ + private function computeScheduleInsertsChangeSets() + { + foreach ($this->entityInsertions as $entity) { + $class = $this->em->getClassMetadata(get_class($entity)); + + $this->computeChangeSet($class, $entity); + } + } + + /** + * Only flush the given entity according to a ruleset that keeps the UoW consistent. + * + * 1. All entities scheduled for insertion, (orphan) removals and changes in collections are processed as well! + * 2. Read Only entities are skipped. + * 3. Proxies are skipped. + * 4. Only if entity is properly managed. + * + * @param object $entity + * @return void + */ + private function computeSingleEntityChangeSet($entity) + { + if ( $this->getEntityState($entity) !== self::STATE_MANAGED) { + throw new \InvalidArgumentException("Entity has to be managed for single computation " . self::objToStr($entity)); + } + + $class = $this->em->getClassMetadata(get_class($entity)); + + if ($class->isChangeTrackingDeferredImplicit()) { + $this->persist($entity); + } + + // Compute changes for INSERTed entities first. This must always happen even in this case. + $this->computeScheduleInsertsChangeSets(); + + if ($class->isReadOnly) { + return; + } + + // Ignore uninitialized proxy objects + if ($entity instanceof Proxy && ! $entity->__isInitialized__) { + return; + } + + // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here. + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityInsertions[$oid]) && isset($this->entityStates[$oid])) { + $this->computeChangeSet($class, $entity); + } + } + + /** + * Executes any extra updates that have been scheduled. + */ + private function executeExtraUpdates() + { + foreach ($this->extraUpdates as $oid => $update) { + list ($entity, $changeset) = $update; + + $this->entityChangeSets[$oid] = $changeset; + $this->getEntityPersister(get_class($entity))->update($entity); + } + } + + /** + * Gets the changeset for an entity. + * + * @return array + */ + public function getEntityChangeSet($entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->entityChangeSets[$oid])) { + return $this->entityChangeSets[$oid]; + } + + return array(); + } + + /** + * Computes the changes that happened to a single entity. + * + * Modifies/populates the following properties: + * + * {@link _originalEntityData} + * If the entity is NEW or MANAGED but not yet fully persisted (only has an id) + * then it was not fetched from the database and therefore we have no original + * entity data yet. All of the current entity data is stored as the original entity data. + * + * {@link _entityChangeSets} + * The changes detected on all properties of the entity are stored there. + * A change is a tuple array where the first entry is the old value and the second + * entry is the new value of the property. Changesets are used by persisters + * to INSERT/UPDATE the persistent entity state. + * + * {@link _entityUpdates} + * If the entity is already fully MANAGED (has been fetched from the database before) + * and any changes to its properties are detected, then a reference to the entity is stored + * there to mark it for an update. + * + * {@link _collectionDeletions} + * If a PersistentCollection has been de-referenced in a fully MANAGED entity, + * then this collection is marked for deletion. + * + * @ignore + * @internal Don't call from the outside. + * @param ClassMetadata $class The class descriptor of the entity. + * @param object $entity The entity for which to compute the changes. + */ + public function computeChangeSet(ClassMetadata $class, $entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->readOnlyObjects[$oid])) { + return; + } + + if ( ! $class->isInheritanceTypeNone()) { + $class = $this->em->getClassMetadata(get_class($entity)); + } + + // Fire PreFlush lifecycle callbacks + if (isset($class->lifecycleCallbacks[Events::preFlush])) { + $class->invokeLifecycleCallbacks(Events::preFlush, $entity); + } + + $actualData = array(); + + foreach ($class->reflFields as $name => $refProp) { + $value = $refProp->getValue($entity); + + if ($class->isCollectionValuedAssociation($name) && $value !== null && ! ($value instanceof PersistentCollection)) { + // If $value is not a Collection then use an ArrayCollection. + if ( ! $value instanceof Collection) { + $value = new ArrayCollection($value); + } + + $assoc = $class->associationMappings[$name]; + + // Inject PersistentCollection + $value = new PersistentCollection( + $this->em, $this->em->getClassMetadata($assoc['targetEntity']), $value + ); + $value->setOwner($entity, $assoc); + $value->setDirty( ! $value->isEmpty()); + + $class->reflFields[$name]->setValue($entity, $value); + + $actualData[$name] = $value; + + continue; + } + + if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) && ($name !== $class->versionField)) { + $actualData[$name] = $value; + } + } + + if ( ! isset($this->originalEntityData[$oid])) { + // Entity is either NEW or MANAGED but not yet fully persisted (only has an id). + // These result in an INSERT. + $this->originalEntityData[$oid] = $actualData; + $changeSet = array(); + + foreach ($actualData as $propName => $actualValue) { + if ( ! isset($class->associationMappings[$propName])) { + $changeSet[$propName] = array(null, $actualValue); + + continue; + } + + $assoc = $class->associationMappings[$propName]; + + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + $changeSet[$propName] = array(null, $actualValue); + } + } + + $this->entityChangeSets[$oid] = $changeSet; + } else { + // Entity is "fully" MANAGED: it was already fully persisted before + // and we have a copy of the original data + $originalData = $this->originalEntityData[$oid]; + $isChangeTrackingNotify = $class->isChangeTrackingNotify(); + $changeSet = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid])) + ? $this->entityChangeSets[$oid] + : array(); + + foreach ($actualData as $propName => $actualValue) { + // skip field, its a partially omitted one! + if ( ! (isset($originalData[$propName]) || array_key_exists($propName, $originalData))) { + continue; + } + + $orgValue = $originalData[$propName]; + + // skip if value havent changed + if ($orgValue === $actualValue) { + continue; + } + + // if regular field + if ( ! isset($class->associationMappings[$propName])) { + if ($isChangeTrackingNotify) { + continue; + } + + $changeSet[$propName] = array($orgValue, $actualValue); + + continue; + } + + $assoc = $class->associationMappings[$propName]; + + // Persistent collection was exchanged with the "originally" + // created one. This can only mean it was cloned and replaced + // on another entity. + if ($actualValue instanceof PersistentCollection) { + $owner = $actualValue->getOwner(); + if ($owner === null) { // cloned + $actualValue->setOwner($entity, $assoc); + } else if ($owner !== $entity) { // no clone, we have to fix + if (!$actualValue->isInitialized()) { + $actualValue->initialize(); // we have to do this otherwise the cols share state + } + $newValue = clone $actualValue; + $newValue->setOwner($entity, $assoc); + $class->reflFields[$propName]->setValue($entity, $newValue); + } + } + + if ($orgValue instanceof PersistentCollection) { + // A PersistentCollection was de-referenced, so delete it. + $coid = spl_object_hash($orgValue); + + if (isset($this->collectionDeletions[$coid])) { + continue; + } + + $this->collectionDeletions[$coid] = $orgValue; + $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored. + + continue; + } + + if ($assoc['type'] & ClassMetadata::TO_ONE) { + if ($assoc['isOwningSide']) { + $changeSet[$propName] = array($orgValue, $actualValue); + } + + if ($orgValue !== null && $assoc['orphanRemoval']) { + $this->scheduleOrphanRemoval($orgValue); + } + } + } + + if ($changeSet) { + $this->entityChangeSets[$oid] = $changeSet; + $this->originalEntityData[$oid] = $actualData; + $this->entityUpdates[$oid] = $entity; + } + } + + // Look for changes in associations of the entity + foreach ($class->associationMappings as $field => $assoc) { + if (($val = $class->reflFields[$field]->getValue($entity)) !== null) { + $this->computeAssociationChanges($assoc, $val); + if (!isset($this->entityChangeSets[$oid]) && + $assoc['isOwningSide'] && + $assoc['type'] == ClassMetadata::MANY_TO_MANY && + $val instanceof PersistentCollection && + $val->isDirty()) { + $this->entityChangeSets[$oid] = array(); + $this->originalEntityData[$oid] = $actualData; + $this->entityUpdates[$oid] = $entity; + } + } + } + } + + /** + * Computes all the changes that have been done to entities and collections + * since the last commit and stores these changes in the _entityChangeSet map + * temporarily for access by the persisters, until the UoW commit is finished. + */ + public function computeChangeSets() + { + // Compute changes for INSERTed entities first. This must always happen. + $this->computeScheduleInsertsChangeSets(); + + // Compute changes for other MANAGED entities. Change tracking policies take effect here. + foreach ($this->identityMap as $className => $entities) { + $class = $this->em->getClassMetadata($className); + + // Skip class if instances are read-only + if ($class->isReadOnly) { + continue; + } + + // If change tracking is explicit or happens through notification, then only compute + // changes on entities of that type that are explicitly marked for synchronization. + switch (true) { + case ($class->isChangeTrackingDeferredImplicit()): + $entitiesToProcess = $entities; + break; + + case (isset($this->scheduledForDirtyCheck[$className])): + $entitiesToProcess = $this->scheduledForDirtyCheck[$className]; + break; + + default: + $entitiesToProcess = array(); + + } + + foreach ($entitiesToProcess as $entity) { + // Ignore uninitialized proxy objects + if ($entity instanceof Proxy && ! $entity->__isInitialized__) { + continue; + } + + // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here. + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityInsertions[$oid]) && isset($this->entityStates[$oid])) { + $this->computeChangeSet($class, $entity); + } + } + } + } + + /** + * Computes the changes of an association. + * + * @param AssociationMapping $assoc + * @param mixed $value The value of the association. + */ + private function computeAssociationChanges($assoc, $value) + { + if ($value instanceof Proxy && ! $value->__isInitialized__) { + return; + } + + if ($value instanceof PersistentCollection && $value->isDirty()) { + $coid = spl_object_hash($value); + + if ($assoc['isOwningSide']) { + $this->collectionUpdates[$coid] = $value; + } + + $this->visitedCollections[$coid] = $value; + } + + // Look through the entities, and in any of their associations, + // for transient (new) entities, recursively. ("Persistence by reachability") + // Unwrap. Uninitialized collections will simply be empty. + $unwrappedValue = ($assoc['type'] & ClassMetadata::TO_ONE) ? array($value) : $value->unwrap(); + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + foreach ($unwrappedValue as $key => $entry) { + $state = $this->getEntityState($entry, self::STATE_NEW); + $oid = spl_object_hash($entry); + + if (!($entry instanceof $assoc['targetEntity'])) { + throw new ORMException(sprintf("Found entity of type %s on association %s#%s, but expecting %s", + get_class($entry), + $assoc['sourceEntity'], + $assoc['fieldName'], + $targetClass->name + )); + } + + switch ($state) { + case self::STATE_NEW: + if ( ! $assoc['isCascadePersist']) { + throw ORMInvalidArgumentException::newEntityFoundThroughRelationship($assoc, $entry); + } + + $this->persistNew($targetClass, $entry); + $this->computeChangeSet($targetClass, $entry); + break; + + case self::STATE_REMOVED: + // Consume the $value as array (it's either an array or an ArrayAccess) + // and remove the element from Collection. + if ($assoc['type'] & ClassMetadata::TO_MANY) { + unset($value[$key]); + } + break; + + case self::STATE_DETACHED: + // Can actually not happen right now as we assume STATE_NEW, + // so the exception will be raised from the DBAL layer (constraint violation). + throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry); + break; + + default: + // MANAGED associated entities are already taken into account + // during changeset calculation anyway, since they are in the identity map. + } + } + } + + private function persistNew($class, $entity) + { + $oid = spl_object_hash($entity); + + if (isset($class->lifecycleCallbacks[Events::prePersist])) { + $class->invokeLifecycleCallbacks(Events::prePersist, $entity); + } + + if ($this->evm->hasListeners(Events::prePersist)) { + $this->evm->dispatchEvent(Events::prePersist, new LifecycleEventArgs($entity, $this->em)); + } + + $idGen = $class->idGenerator; + + if ( ! $idGen->isPostInsertGenerator()) { + $idValue = $idGen->generate($this->em, $entity); + + if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) { + $idValue = array($class->identifier[0] => $idValue); + + $class->setIdentifierValues($entity, $idValue); + } + + $this->entityIdentifiers[$oid] = $idValue; + } + + $this->entityStates[$oid] = self::STATE_MANAGED; + + $this->scheduleForInsert($entity); + } + + /** + * INTERNAL: + * Computes the changeset of an individual entity, independently of the + * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit(). + * + * The passed entity must be a managed entity. If the entity already has a change set + * because this method is invoked during a commit cycle then the change sets are added. + * whereby changes detected in this method prevail. + * + * @ignore + * @param ClassMetadata $class The class descriptor of the entity. + * @param object $entity The entity for which to (re)calculate the change set. + * @throws InvalidArgumentException If the passed entity is not MANAGED. + */ + public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity) + { + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) { + throw ORMInvalidArgumentException::entityNotManaged($entity); + } + + // skip if change tracking is "NOTIFY" + if ($class->isChangeTrackingNotify()) { + return; + } + + if ( ! $class->isInheritanceTypeNone()) { + $class = $this->em->getClassMetadata(get_class($entity)); + } + + $actualData = array(); + + foreach ($class->reflFields as $name => $refProp) { + if ( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) { + $actualData[$name] = $refProp->getValue($entity); + } + } + + $originalData = $this->originalEntityData[$oid]; + $changeSet = array(); + + foreach ($actualData as $propName => $actualValue) { + $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; + + if (is_object($orgValue) && $orgValue !== $actualValue) { + $changeSet[$propName] = array($orgValue, $actualValue); + } else if ($orgValue != $actualValue || ($orgValue === null ^ $actualValue === null)) { + $changeSet[$propName] = array($orgValue, $actualValue); + } + } + + if ($changeSet) { + if (isset($this->entityChangeSets[$oid])) { + $this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet); + } + + $this->originalEntityData[$oid] = $actualData; + } + } + + /** + * Executes all entity insertions for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + */ + private function executeInserts($class) + { + $className = $class->name; + $persister = $this->getEntityPersister($className); + $entities = array(); + + $hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postPersist]); + $hasListeners = $this->evm->hasListeners(Events::postPersist); + + foreach ($this->entityInsertions as $oid => $entity) { + if (get_class($entity) !== $className) { + continue; + } + + $persister->addInsert($entity); + + unset($this->entityInsertions[$oid]); + + if ($hasLifecycleCallbacks || $hasListeners) { + $entities[] = $entity; + } + } + + $postInsertIds = $persister->executeInserts(); + + if ($postInsertIds) { + // Persister returned post-insert IDs + foreach ($postInsertIds as $id => $entity) { + $oid = spl_object_hash($entity); + $idField = $class->identifier[0]; + + $class->reflFields[$idField]->setValue($entity, $id); + + $this->entityIdentifiers[$oid] = array($idField => $id); + $this->entityStates[$oid] = self::STATE_MANAGED; + $this->originalEntityData[$oid][$idField] = $id; + + $this->addToIdentityMap($entity); + } + } + + foreach ($entities as $entity) { + if ($hasLifecycleCallbacks) { + $class->invokeLifecycleCallbacks(Events::postPersist, $entity); + } + + if ($hasListeners) { + $this->evm->dispatchEvent(Events::postPersist, new LifecycleEventArgs($entity, $this->em)); + } + } + } + + /** + * Executes all entity updates for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + */ + private function executeUpdates($class) + { + $className = $class->name; + $persister = $this->getEntityPersister($className); + + $hasPreUpdateLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::preUpdate]); + $hasPreUpdateListeners = $this->evm->hasListeners(Events::preUpdate); + + $hasPostUpdateLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postUpdate]); + $hasPostUpdateListeners = $this->evm->hasListeners(Events::postUpdate); + + foreach ($this->entityUpdates as $oid => $entity) { + if ( ! (get_class($entity) === $className || $entity instanceof Proxy && get_parent_class($entity) === $className)) { + continue; + } + + if ($hasPreUpdateLifecycleCallbacks) { + $class->invokeLifecycleCallbacks(Events::preUpdate, $entity); + + $this->recomputeSingleEntityChangeSet($class, $entity); + } + + if ($hasPreUpdateListeners) { + $this->evm->dispatchEvent( + Events::preUpdate, + new Event\PreUpdateEventArgs($entity, $this->em, $this->entityChangeSets[$oid]) + ); + } + + if ($this->entityChangeSets[$oid]) { + $persister->update($entity); + } + + unset($this->entityUpdates[$oid]); + + if ($hasPostUpdateLifecycleCallbacks) { + $class->invokeLifecycleCallbacks(Events::postUpdate, $entity); + } + + if ($hasPostUpdateListeners) { + $this->evm->dispatchEvent(Events::postUpdate, new LifecycleEventArgs($entity, $this->em)); + } + } + } + + /** + * Executes all entity deletions for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + */ + private function executeDeletions($class) + { + $className = $class->name; + $persister = $this->getEntityPersister($className); + + $hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postRemove]); + $hasListeners = $this->evm->hasListeners(Events::postRemove); + + foreach ($this->entityDeletions as $oid => $entity) { + if ( ! (get_class($entity) == $className || $entity instanceof Proxy && get_parent_class($entity) == $className)) { + continue; + } + + $persister->delete($entity); + + unset( + $this->entityDeletions[$oid], + $this->entityIdentifiers[$oid], + $this->originalEntityData[$oid], + $this->entityStates[$oid] + ); + + // Entity with this $oid after deletion treated as NEW, even if the $oid + // is obtained by a new entity because the old one went out of scope. + //$this->entityStates[$oid] = self::STATE_NEW; + if ( ! $class->isIdentifierNatural()) { + $class->reflFields[$class->identifier[0]]->setValue($entity, null); + } + + if ($hasLifecycleCallbacks) { + $class->invokeLifecycleCallbacks(Events::postRemove, $entity); + } + + if ($hasListeners) { + $this->evm->dispatchEvent(Events::postRemove, new LifecycleEventArgs($entity, $this->em)); + } + } + } + + /** + * Gets the commit order. + * + * @return array + */ + private function getCommitOrder(array $entityChangeSet = null) + { + if ($entityChangeSet === null) { + $entityChangeSet = array_merge($this->entityInsertions, $this->entityUpdates, $this->entityDeletions); + } + + $calc = $this->getCommitOrderCalculator(); + + // See if there are any new classes in the changeset, that are not in the + // commit order graph yet (dont have a node). + // We have to inspect changeSet to be able to correctly build dependencies. + // It is not possible to use IdentityMap here because post inserted ids + // are not yet available. + $newNodes = array(); + + foreach ($entityChangeSet as $oid => $entity) { + $className = get_class($entity); + + if ($calc->hasClass($className)) { + continue; + } + + $class = $this->em->getClassMetadata($className); + $calc->addClass($class); + + $newNodes[] = $class; + } + + // Calculate dependencies for new nodes + while ($class = array_pop($newNodes)) { + foreach ($class->associationMappings as $assoc) { + if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) { + continue; + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + if ( ! $calc->hasClass($targetClass->name)) { + $calc->addClass($targetClass); + + $newNodes[] = $targetClass; + } + + $calc->addDependency($targetClass, $class); + + // If the target class has mapped subclasses, these share the same dependency. + if ( ! $targetClass->subClasses) { + continue; + } + + foreach ($targetClass->subClasses as $subClassName) { + $targetSubClass = $this->em->getClassMetadata($subClassName); + + if ( ! $calc->hasClass($subClassName)) { + $calc->addClass($targetSubClass); + + $newNodes[] = $targetSubClass; + } + + $calc->addDependency($targetSubClass, $class); + } + } + } + + return $calc->getCommitOrder(); + } + + /** + * Schedules an entity for insertion into the database. + * If the entity already has an identifier, it will be added to the identity map. + * + * @param object $entity The entity to schedule for insertion. + */ + public function scheduleForInsert($entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->entityUpdates[$oid])) { + throw new InvalidArgumentException("Dirty entity can not be scheduled for insertion."); + } + + if (isset($this->entityDeletions[$oid])) { + throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity); + } + if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) { + throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity); + } + + if (isset($this->entityInsertions[$oid])) { + throw ORMInvalidArgumentException::scheduleInsertTwice($entity); + } + + $this->entityInsertions[$oid] = $entity; + + if (isset($this->entityIdentifiers[$oid])) { + $this->addToIdentityMap($entity); + } + } + + /** + * Checks whether an entity is scheduled for insertion. + * + * @param object $entity + * @return boolean + */ + public function isScheduledForInsert($entity) + { + return isset($this->entityInsertions[spl_object_hash($entity)]); + } + + /** + * Schedules an entity for being updated. + * + * @param object $entity The entity to schedule for being updated. + */ + public function scheduleForUpdate($entity) + { + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityIdentifiers[$oid])) { + throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "scheduling for update"); + } + + if (isset($this->entityDeletions[$oid])) { + throw ORMInvalidArgumentException::entityIsRemoved($entity, "schedule for update"); + } + + if ( ! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid])) { + $this->entityUpdates[$oid] = $entity; + } + } + + /** + * INTERNAL: + * Schedules an extra update that will be executed immediately after the + * regular entity updates within the currently running commit cycle. + * + * Extra updates for entities are stored as (entity, changeset) tuples. + * + * @ignore + * @param object $entity The entity for which to schedule an extra update. + * @param array $changeset The changeset of the entity (what to update). + */ + public function scheduleExtraUpdate($entity, array $changeset) + { + $oid = spl_object_hash($entity); + $extraUpdate = array($entity, $changeset); + + if (isset($this->extraUpdates[$oid])) { + list($ignored, $changeset2) = $this->extraUpdates[$oid]; + + $extraUpdate = array($entity, $changeset + $changeset2); + } + + $this->extraUpdates[$oid] = $extraUpdate; + } + + /** + * Checks whether an entity is registered as dirty in the unit of work. + * Note: Is not very useful currently as dirty entities are only registered + * at commit time. + * + * @param object $entity + * @return boolean + */ + public function isScheduledForUpdate($entity) + { + return isset($this->entityUpdates[spl_object_hash($entity)]); + } + + + /** + * Checks whether an entity is registered to be checked in the unit of work. + * + * @param object $entity + * @return boolean + */ + public function isScheduledForDirtyCheck($entity) + { + $rootEntityName = $this->em->getClassMetadata(get_class($entity))->rootEntityName; + + return isset($this->scheduledForDirtyCheck[$rootEntityName][spl_object_hash($entity)]); + } + + /** + * INTERNAL: + * Schedules an entity for deletion. + * + * @param object $entity + */ + public function scheduleForDelete($entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->entityInsertions[$oid])) { + if ($this->isInIdentityMap($entity)) { + $this->removeFromIdentityMap($entity); + } + + unset($this->entityInsertions[$oid], $this->entityStates[$oid]); + + return; // entity has not been persisted yet, so nothing more to do. + } + + if ( ! $this->isInIdentityMap($entity)) { + return; + } + + $this->removeFromIdentityMap($entity); + + if (isset($this->entityUpdates[$oid])) { + unset($this->entityUpdates[$oid]); + } + + if ( ! isset($this->entityDeletions[$oid])) { + $this->entityDeletions[$oid] = $entity; + $this->entityStates[$oid] = self::STATE_REMOVED; + } + } + + /** + * Checks whether an entity is registered as removed/deleted with the unit + * of work. + * + * @param object $entity + * @return boolean + */ + public function isScheduledForDelete($entity) + { + return isset($this->entityDeletions[spl_object_hash($entity)]); + } + + /** + * Checks whether an entity is scheduled for insertion, update or deletion. + * + * @param $entity + * @return boolean + */ + public function isEntityScheduled($entity) + { + $oid = spl_object_hash($entity); + + return isset($this->entityInsertions[$oid]) + || isset($this->entityUpdates[$oid]) + || isset($this->entityDeletions[$oid]); + } + + /** + * INTERNAL: + * Registers an entity in the identity map. + * Note that entities in a hierarchy are registered with the class name of + * the root entity. + * + * @ignore + * @param object $entity The entity to register. + * @return boolean TRUE if the registration was successful, FALSE if the identity of + * the entity in question is already managed. + */ + public function addToIdentityMap($entity) + { + $classMetadata = $this->em->getClassMetadata(get_class($entity)); + $idHash = implode(' ', $this->entityIdentifiers[spl_object_hash($entity)]); + + if ($idHash === '') { + throw ORMInvalidArgumentException::entityWithoutIdentity($classMetadata->name, $entity); + } + + $className = $classMetadata->rootEntityName; + + if (isset($this->identityMap[$className][$idHash])) { + return false; + } + + $this->identityMap[$className][$idHash] = $entity; + + if ($entity instanceof NotifyPropertyChanged) { + $entity->addPropertyChangedListener($this); + } + + return true; + } + + /** + * Gets the state of an entity with regard to the current unit of work. + * + * @param object $entity + * @param integer $assume The state to assume if the state is not yet known (not MANAGED or REMOVED). + * This parameter can be set to improve performance of entity state detection + * by potentially avoiding a database lookup if the distinction between NEW and DETACHED + * is either known or does not matter for the caller of the method. + * @return int The entity state. + */ + public function getEntityState($entity, $assume = null) + { + $oid = spl_object_hash($entity); + + if (isset($this->entityStates[$oid])) { + return $this->entityStates[$oid]; + } + + if ($assume !== null) { + return $assume; + } + + // State can only be NEW or DETACHED, because MANAGED/REMOVED states are known. + // Note that you can not remember the NEW or DETACHED state in _entityStates since + // the UoW does not hold references to such objects and the object hash can be reused. + // More generally because the state may "change" between NEW/DETACHED without the UoW being aware of it. + $class = $this->em->getClassMetadata(get_class($entity)); + $id = $class->getIdentifierValues($entity); + + if ( ! $id) { + return self::STATE_NEW; + } + + switch (true) { + case ($class->isIdentifierNatural()); + // Check for a version field, if available, to avoid a db lookup. + if ($class->isVersioned) { + return ($class->getFieldValue($entity, $class->versionField)) + ? self::STATE_DETACHED + : self::STATE_NEW; + } + + // Last try before db lookup: check the identity map. + if ($this->tryGetById($id, $class->rootEntityName)) { + return self::STATE_DETACHED; + } + + // db lookup + if ($this->getEntityPersister(get_class($entity))->exists($entity)) { + return self::STATE_DETACHED; + } + + return self::STATE_NEW; + + case ( ! $class->idGenerator->isPostInsertGenerator()): + // if we have a pre insert generator we can't be sure that having an id + // really means that the entity exists. We have to verify this through + // the last resort: a db lookup + + // Last try before db lookup: check the identity map. + if ($this->tryGetById($id, $class->rootEntityName)) { + return self::STATE_DETACHED; + } + + // db lookup + if ($this->getEntityPersister(get_class($entity))->exists($entity)) { + return self::STATE_DETACHED; + } + + return self::STATE_NEW; + + default: + return self::STATE_DETACHED; + } + } + + /** + * INTERNAL: + * Removes an entity from the identity map. This effectively detaches the + * entity from the persistence management of Doctrine. + * + * @ignore + * @param object $entity + * @return boolean + */ + public function removeFromIdentityMap($entity) + { + $oid = spl_object_hash($entity); + $classMetadata = $this->em->getClassMetadata(get_class($entity)); + $idHash = implode(' ', $this->entityIdentifiers[$oid]); + + if ($idHash === '') { + throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "remove from identity map"); + } + + $className = $classMetadata->rootEntityName; + + if (isset($this->identityMap[$className][$idHash])) { + unset($this->identityMap[$className][$idHash]); + unset($this->readOnlyObjects[$oid]); + + //$this->entityStates[$oid] = self::STATE_DETACHED; + + return true; + } + + return false; + } + + /** + * INTERNAL: + * Gets an entity in the identity map by its identifier hash. + * + * @ignore + * @param string $idHash + * @param string $rootClassName + * @return object + */ + public function getByIdHash($idHash, $rootClassName) + { + return $this->identityMap[$rootClassName][$idHash]; + } + + /** + * INTERNAL: + * Tries to get an entity by its identifier hash. If no entity is found for + * the given hash, FALSE is returned. + * + * @ignore + * @param string $idHash + * @param string $rootClassName + * @return mixed The found entity or FALSE. + */ + public function tryGetByIdHash($idHash, $rootClassName) + { + if (isset($this->identityMap[$rootClassName][$idHash])) { + return $this->identityMap[$rootClassName][$idHash]; + } + + return false; + } + + /** + * Checks whether an entity is registered in the identity map of this UnitOfWork. + * + * @param object $entity + * @return boolean + */ + public function isInIdentityMap($entity) + { + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityIdentifiers[$oid])) { + return false; + } + + $classMetadata = $this->em->getClassMetadata(get_class($entity)); + $idHash = implode(' ', $this->entityIdentifiers[$oid]); + + if ($idHash === '') { + return false; + } + + return isset($this->identityMap[$classMetadata->rootEntityName][$idHash]); + } + + /** + * INTERNAL: + * Checks whether an identifier hash exists in the identity map. + * + * @ignore + * @param string $idHash + * @param string $rootClassName + * @return boolean + */ + public function containsIdHash($idHash, $rootClassName) + { + return isset($this->identityMap[$rootClassName][$idHash]); + } + + /** + * Persists an entity as part of the current unit of work. + * + * @param object $entity The entity to persist. + */ + public function persist($entity) + { + $visited = array(); + + $this->doPersist($entity, $visited); + } + + /** + * Persists an entity as part of the current unit of work. + * + * This method is internally called during persist() cascades as it tracks + * the already visited entities to prevent infinite recursions. + * + * @param object $entity The entity to persist. + * @param array $visited The already visited entities. + */ + private function doPersist($entity, array &$visited) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // Mark visited + + $class = $this->em->getClassMetadata(get_class($entity)); + + // We assume NEW, so DETACHED entities result in an exception on flush (constraint violation). + // If we would detect DETACHED here we would throw an exception anyway with the same + // consequences (not recoverable/programming error), so just assuming NEW here + // lets us avoid some database lookups for entities with natural identifiers. + $entityState = $this->getEntityState($entity, self::STATE_NEW); + + switch ($entityState) { + case self::STATE_MANAGED: + // Nothing to do, except if policy is "deferred explicit" + if ($class->isChangeTrackingDeferredExplicit()) { + $this->scheduleForDirtyCheck($entity); + } + break; + + case self::STATE_NEW: + $this->persistNew($class, $entity); + break; + + case self::STATE_REMOVED: + // Entity becomes managed again + unset($this->entityDeletions[$oid]); + + $this->entityStates[$oid] = self::STATE_MANAGED; + break; + + case self::STATE_DETACHED: + // Can actually not happen right now since we assume STATE_NEW. + throw ORMInvalidArgumentException::detachedEntityCannot($entity, "persisted"); + + default: + throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity)); + } + + $this->cascadePersist($entity, $visited); + } + + /** + * Deletes an entity as part of the current unit of work. + * + * @param object $entity The entity to remove. + */ + public function remove($entity) + { + $visited = array(); + + $this->doRemove($entity, $visited); + } + + /** + * Deletes an entity as part of the current unit of work. + * + * This method is internally called during delete() cascades as it tracks + * the already visited entities to prevent infinite recursions. + * + * @param object $entity The entity to delete. + * @param array $visited The map of the already visited entities. + * @throws InvalidArgumentException If the instance is a detached entity. + */ + private function doRemove($entity, array &$visited) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // mark visited + + // Cascade first, because scheduleForDelete() removes the entity from the identity map, which + // can cause problems when a lazy proxy has to be initialized for the cascade operation. + $this->cascadeRemove($entity, $visited); + + $class = $this->em->getClassMetadata(get_class($entity)); + $entityState = $this->getEntityState($entity); + + switch ($entityState) { + case self::STATE_NEW: + case self::STATE_REMOVED: + // nothing to do + break; + + case self::STATE_MANAGED: + if (isset($class->lifecycleCallbacks[Events::preRemove])) { + $class->invokeLifecycleCallbacks(Events::preRemove, $entity); + } + + if ($this->evm->hasListeners(Events::preRemove)) { + $this->evm->dispatchEvent(Events::preRemove, new LifecycleEventArgs($entity, $this->em)); + } + + $this->scheduleForDelete($entity); + break; + + case self::STATE_DETACHED: + throw ORMInvalidArgumentException::detachedEntityCannot($entity, "removed"); + default: + throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity)); + } + + } + + /** + * Merges the state of the given detached entity into this UnitOfWork. + * + * @param object $entity + * @return object The managed copy of the entity. + * @throws OptimisticLockException If the entity uses optimistic locking through a version + * attribute and the version check against the managed copy fails. + * + * @todo Require active transaction!? OptimisticLockException may result in undefined state!? + */ + public function merge($entity) + { + $visited = array(); + + return $this->doMerge($entity, $visited); + } + + /** + * Executes a merge operation on an entity. + * + * @param object $entity + * @param array $visited + * @return object The managed copy of the entity. + * @throws OptimisticLockException If the entity uses optimistic locking through a version + * attribute and the version check against the managed copy fails. + * @throws InvalidArgumentException If the entity instance is NEW. + */ + private function doMerge($entity, array &$visited, $prevManagedCopy = null, $assoc = null) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // mark visited + + $class = $this->em->getClassMetadata(get_class($entity)); + + // First we assume DETACHED, although it can still be NEW but we can avoid + // an extra db-roundtrip this way. If it is not MANAGED but has an identity, + // we need to fetch it from the db anyway in order to merge. + // MANAGED entities are ignored by the merge operation. + $managedCopy = $entity; + + if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) { + if ($entity instanceof Proxy && ! $entity->__isInitialized__) { + $entity->__load(); + } + + // Try to look the entity up in the identity map. + $id = $class->getIdentifierValues($entity); + + // If there is no ID, it is actually NEW. + if ( ! $id) { + $managedCopy = $this->newInstance($class); + + $this->persistNew($class, $managedCopy); + } else { + $flatId = $id; + if ($class->containsForeignIdentifier) { + // convert foreign identifiers into scalar foreign key + // values to avoid object to string conversion failures. + foreach ($id as $idField => $idValue) { + if (isset($class->associationMappings[$idField])) { + $targetClassMetadata = $this->em->getClassMetadata($class->associationMappings[$idField]['targetEntity']); + $associatedId = $this->getEntityIdentifier($idValue); + $flatId[$idField] = $associatedId[$targetClassMetadata->identifier[0]]; + } + } + } + + $managedCopy = $this->tryGetById($flatId, $class->rootEntityName); + + if ($managedCopy) { + // We have the entity in-memory already, just make sure its not removed. + if ($this->getEntityState($managedCopy) == self::STATE_REMOVED) { + throw ORMInvalidArgumentException::entityIsRemoved($managedCopy, "merge"); + } + } else { + // We need to fetch the managed copy in order to merge. + $managedCopy = $this->em->find($class->name, $flatId); + } + + if ($managedCopy === null) { + // If the identifier is ASSIGNED, it is NEW, otherwise an error + // since the managed entity was not found. + if ( ! $class->isIdentifierNatural()) { + throw new EntityNotFoundException; + } + + $managedCopy = $this->newInstance($class); + $class->setIdentifierValues($managedCopy, $id); + + $this->persistNew($class, $managedCopy); + } else { + if ($managedCopy instanceof Proxy && ! $managedCopy->__isInitialized__) { + $managedCopy->__load(); + } + } + } + + if ($class->isVersioned) { + $managedCopyVersion = $class->reflFields[$class->versionField]->getValue($managedCopy); + $entityVersion = $class->reflFields[$class->versionField]->getValue($entity); + + // Throw exception if versions dont match. + if ($managedCopyVersion != $entityVersion) { + throw OptimisticLockException::lockFailedVersionMissmatch($entity, $entityVersion, $managedCopyVersion); + } + } + + // Merge state of $entity into existing (managed) entity + foreach ($class->reflFields as $name => $prop) { + if ( ! isset($class->associationMappings[$name])) { + if ( ! $class->isIdentifier($name)) { + $prop->setValue($managedCopy, $prop->getValue($entity)); + } + } else { + $assoc2 = $class->associationMappings[$name]; + if ($assoc2['type'] & ClassMetadata::TO_ONE) { + $other = $prop->getValue($entity); + if ($other === null) { + $prop->setValue($managedCopy, null); + } else if ($other instanceof Proxy && !$other->__isInitialized__) { + // do not merge fields marked lazy that have not been fetched. + continue; + } else if ( ! $assoc2['isCascadeMerge']) { + if ($this->getEntityState($other, self::STATE_DETACHED) !== self::STATE_MANAGED) { + $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']); + $relatedId = $targetClass->getIdentifierValues($other); + + if ($targetClass->subClasses) { + $other = $this->em->find($targetClass->name, $relatedId); + } else { + $other = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $relatedId); + $this->registerManaged($other, $relatedId, array()); + } + } + $prop->setValue($managedCopy, $other); + } + } else { + $mergeCol = $prop->getValue($entity); + if ($mergeCol instanceof PersistentCollection && !$mergeCol->isInitialized()) { + // do not merge fields marked lazy that have not been fetched. + // keep the lazy persistent collection of the managed copy. + continue; + } + + $managedCol = $prop->getValue($managedCopy); + if (!$managedCol) { + $managedCol = new PersistentCollection($this->em, + $this->em->getClassMetadata($assoc2['targetEntity']), + new ArrayCollection + ); + $managedCol->setOwner($managedCopy, $assoc2); + $prop->setValue($managedCopy, $managedCol); + $this->originalEntityData[$oid][$name] = $managedCol; + } + if ($assoc2['isCascadeMerge']) { + $managedCol->initialize(); + + // clear and set dirty a managed collection if its not also the same collection to merge from. + if (!$managedCol->isEmpty() && $managedCol !== $mergeCol) { + $managedCol->unwrap()->clear(); + $managedCol->setDirty(true); + + if ($assoc2['isOwningSide'] && $assoc2['type'] == ClassMetadata::MANY_TO_MANY && $class->isChangeTrackingNotify()) { + $this->scheduleForDirtyCheck($managedCopy); + } + } + } + } + } + + if ($class->isChangeTrackingNotify()) { + // Just treat all properties as changed, there is no other choice. + $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy)); + } + } + + if ($class->isChangeTrackingDeferredExplicit()) { + $this->scheduleForDirtyCheck($entity); + } + } + + if ($prevManagedCopy !== null) { + $assocField = $assoc['fieldName']; + $prevClass = $this->em->getClassMetadata(get_class($prevManagedCopy)); + + if ($assoc['type'] & ClassMetadata::TO_ONE) { + $prevClass->reflFields[$assocField]->setValue($prevManagedCopy, $managedCopy); + } else { + $prevClass->reflFields[$assocField]->getValue($prevManagedCopy)->add($managedCopy); + + if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) { + $class->reflFields[$assoc['mappedBy']]->setValue($managedCopy, $prevManagedCopy); + } + } + } + + // Mark the managed copy visited as well + $visited[spl_object_hash($managedCopy)] = true; + + $this->cascadeMerge($entity, $managedCopy, $visited); + + return $managedCopy; + } + + /** + * Detaches an entity from the persistence management. It's persistence will + * no longer be managed by Doctrine. + * + * @param object $entity The entity to detach. + */ + public function detach($entity) + { + $visited = array(); + + $this->doDetach($entity, $visited); + } + + /** + * Executes a detach operation on the given entity. + * + * @param object $entity + * @param array $visited + * @param boolean $noCascade if true, don't cascade detach operation + */ + private function doDetach($entity, array &$visited, $noCascade = false) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // mark visited + + switch ($this->getEntityState($entity, self::STATE_DETACHED)) { + case self::STATE_MANAGED: + if ($this->isInIdentityMap($entity)) { + $this->removeFromIdentityMap($entity); + } + + unset( + $this->entityInsertions[$oid], + $this->entityUpdates[$oid], + $this->entityDeletions[$oid], + $this->entityIdentifiers[$oid], + $this->entityStates[$oid], + $this->originalEntityData[$oid] + ); + break; + case self::STATE_NEW: + case self::STATE_DETACHED: + return; + } + + if ( ! $noCascade) { + $this->cascadeDetach($entity, $visited); + } + } + + /** + * Refreshes the state of the given entity from the database, overwriting + * any local, unpersisted changes. + * + * @param object $entity The entity to refresh. + * @throws InvalidArgumentException If the entity is not MANAGED. + */ + public function refresh($entity) + { + $visited = array(); + + $this->doRefresh($entity, $visited); + } + + /** + * Executes a refresh operation on an entity. + * + * @param object $entity The entity to refresh. + * @param array $visited The already visited entities during cascades. + * @throws InvalidArgumentException If the entity is not MANAGED. + */ + private function doRefresh($entity, array &$visited) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // mark visited + + $class = $this->em->getClassMetadata(get_class($entity)); + + if ($this->getEntityState($entity) !== self::STATE_MANAGED) { + throw ORMInvalidArgumentException::entityNotManaged($entity); + } + + $this->getEntityPersister($class->name)->refresh( + array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]), + $entity + ); + + $this->cascadeRefresh($entity, $visited); + } + + /** + * Cascades a refresh operation to associated entities. + * + * @param object $entity + * @param array $visited + */ + private function cascadeRefresh($entity, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeRefresh']; } + ); + + foreach ($associationMappings as $assoc) { + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + switch (true) { + case ($relatedEntities instanceof PersistentCollection): + // Unwrap so that foreach() does not initialize + $relatedEntities = $relatedEntities->unwrap(); + // break; is commented intentionally! + + case ($relatedEntities instanceof Collection): + case (is_array($relatedEntities)): + foreach ($relatedEntities as $relatedEntity) { + $this->doRefresh($relatedEntity, $visited); + } + break; + + case ($relatedEntities !== null): + $this->doRefresh($relatedEntities, $visited); + break; + + default: + // Do nothing + } + } + } + + /** + * Cascades a detach operation to associated entities. + * + * @param object $entity + * @param array $visited + */ + private function cascadeDetach($entity, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeDetach']; } + ); + + foreach ($associationMappings as $assoc) { + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + switch (true) { + case ($relatedEntities instanceof PersistentCollection): + // Unwrap so that foreach() does not initialize + $relatedEntities = $relatedEntities->unwrap(); + // break; is commented intentionally! + + case ($relatedEntities instanceof Collection): + case (is_array($relatedEntities)): + foreach ($relatedEntities as $relatedEntity) { + $this->doDetach($relatedEntity, $visited); + } + break; + + case ($relatedEntities !== null): + $this->doDetach($relatedEntities, $visited); + break; + + default: + // Do nothing + } + } + } + + /** + * Cascades a merge operation to associated entities. + * + * @param object $entity + * @param object $managedCopy + * @param array $visited + */ + private function cascadeMerge($entity, $managedCopy, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeMerge']; } + ); + + foreach ($associationMappings as $assoc) { + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + if ($relatedEntities instanceof Collection) { + if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) { + continue; + } + + if ($relatedEntities instanceof PersistentCollection) { + // Unwrap so that foreach() does not initialize + $relatedEntities = $relatedEntities->unwrap(); + } + + foreach ($relatedEntities as $relatedEntity) { + $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc); + } + } else if ($relatedEntities !== null) { + $this->doMerge($relatedEntities, $visited, $managedCopy, $assoc); + } + } + } + + /** + * Cascades the save operation to associated entities. + * + * @param object $entity + * @param array $visited + * @param array $insertNow + */ + private function cascadePersist($entity, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadePersist']; } + ); + + foreach ($associationMappings as $assoc) { + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + switch (true) { + case ($relatedEntities instanceof PersistentCollection): + // Unwrap so that foreach() does not initialize + $relatedEntities = $relatedEntities->unwrap(); + // break; is commented intentionally! + + case ($relatedEntities instanceof Collection): + case (is_array($relatedEntities)): + foreach ($relatedEntities as $relatedEntity) { + $this->doPersist($relatedEntity, $visited); + } + break; + + case ($relatedEntities !== null): + $this->doPersist($relatedEntities, $visited); + break; + + default: + // Do nothing + } + } + } + + /** + * Cascades the delete operation to associated entities. + * + * @param object $entity + * @param array $visited + */ + private function cascadeRemove($entity, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeRemove']; } + ); + + foreach ($associationMappings as $assoc) { + if ($entity instanceof Proxy && !$entity->__isInitialized__) { + $entity->__load(); + } + + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + switch (true) { + case ($relatedEntities instanceof Collection): + case (is_array($relatedEntities)): + // If its a PersistentCollection initialization is intended! No unwrap! + foreach ($relatedEntities as $relatedEntity) { + $this->doRemove($relatedEntity, $visited); + } + break; + + case ($relatedEntities !== null): + $this->doRemove($relatedEntities, $visited); + break; + + default: + // Do nothing + } + } + } + + /** + * Acquire a lock on the given entity. + * + * @param object $entity + * @param int $lockMode + * @param int $lockVersion + */ + public function lock($entity, $lockMode, $lockVersion = null) + { + if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) { + throw ORMInvalidArgumentException::entityNotManaged($entity); + } + + $entityName = get_class($entity); + $class = $this->em->getClassMetadata($entityName); + + switch ($lockMode) { + case \Doctrine\DBAL\LockMode::OPTIMISTIC; + if ( ! $class->isVersioned) { + throw OptimisticLockException::notVersioned($entityName); + } + + if ($lockVersion === null) { + return; + } + + $entityVersion = $class->reflFields[$class->versionField]->getValue($entity); + + if ($entityVersion != $lockVersion) { + throw OptimisticLockException::lockFailedVersionMissmatch($entity, $lockVersion, $entityVersion); + } + + break; + + case \Doctrine\DBAL\LockMode::PESSIMISTIC_READ: + case \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE: + if (!$this->em->getConnection()->isTransactionActive()) { + throw TransactionRequiredException::transactionRequired(); + } + + $oid = spl_object_hash($entity); + + $this->getEntityPersister($class->name)->lock( + array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]), + $lockMode + ); + break; + + default: + // Do nothing + } + } + + /** + * Gets the CommitOrderCalculator used by the UnitOfWork to order commits. + * + * @return \Doctrine\ORM\Internal\CommitOrderCalculator + */ + public function getCommitOrderCalculator() + { + if ($this->commitOrderCalculator === null) { + $this->commitOrderCalculator = new Internal\CommitOrderCalculator; + } + + return $this->commitOrderCalculator; + } + + /** + * Clears the UnitOfWork. + * + * @param string $entityName if given, only entities of this type will get detached + */ + public function clear($entityName = null) + { + if ($entityName === null) { + $this->identityMap = + $this->entityIdentifiers = + $this->originalEntityData = + $this->entityChangeSets = + $this->entityStates = + $this->scheduledForDirtyCheck = + $this->entityInsertions = + $this->entityUpdates = + $this->entityDeletions = + $this->collectionDeletions = + $this->collectionUpdates = + $this->extraUpdates = + $this->readOnlyObjects = + $this->orphanRemovals = array(); + + if ($this->commitOrderCalculator !== null) { + $this->commitOrderCalculator->clear(); + } + } else { + $visited = array(); + foreach ($this->identityMap as $className => $entities) { + if ($className === $entityName) { + foreach ($entities as $entity) { + $this->doDetach($entity, $visited, true); + } + } + } + } + + if ($this->evm->hasListeners(Events::onClear)) { + $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName)); + } + } + + /** + * INTERNAL: + * Schedules an orphaned entity for removal. The remove() operation will be + * invoked on that entity at the beginning of the next commit of this + * UnitOfWork. + * + * @ignore + * @param object $entity + */ + public function scheduleOrphanRemoval($entity) + { + $this->orphanRemovals[spl_object_hash($entity)] = $entity; + } + + /** + * INTERNAL: + * Schedules a complete collection for removal when this UnitOfWork commits. + * + * @param PersistentCollection $coll + */ + public function scheduleCollectionDeletion(PersistentCollection $coll) + { + $coid = spl_object_hash($coll); + + //TODO: if $coll is already scheduled for recreation ... what to do? + // Just remove $coll from the scheduled recreations? + if (isset($this->collectionUpdates[$coid])) { + unset($this->collectionUpdates[$coid]); + } + + $this->collectionDeletions[$coid] = $coll; + } + + public function isCollectionScheduledForDeletion(PersistentCollection $coll) + { + return isset($this->collectionsDeletions[spl_object_hash($coll)]); + } + + /** + * @param ClassMetadata $class + */ + private function newInstance($class) + { + $entity = $class->newInstance(); + + if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) { + $entity->injectObjectManager($this->em, $class); + } + + return $entity; + } + + /** + * INTERNAL: + * Creates an entity. Used for reconstitution of persistent entities. + * + * @ignore + * @param string $className The name of the entity class. + * @param array $data The data for the entity. + * @param array $hints Any hints to account for during reconstitution/lookup of the entity. + * @return object The managed entity instance. + * @internal Highly performance-sensitive method. + * + * @todo Rename: getOrCreateEntity + */ + public function createEntity($className, array $data, &$hints = array()) + { + $class = $this->em->getClassMetadata($className); + //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]); + + if ($class->isIdentifierComposite) { + $id = array(); + + foreach ($class->identifier as $fieldName) { + $id[$fieldName] = isset($class->associationMappings[$fieldName]) + ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] + : $data[$fieldName]; + } + + $idHash = implode(' ', $id); + } else { + $idHash = isset($class->associationMappings[$class->identifier[0]]) + ? $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']] + : $data[$class->identifier[0]]; + + $id = array($class->identifier[0] => $idHash); + } + + if (isset($this->identityMap[$class->rootEntityName][$idHash])) { + $entity = $this->identityMap[$class->rootEntityName][$idHash]; + $oid = spl_object_hash($entity); + + if ($entity instanceof Proxy && ! $entity->__isInitialized__) { + $entity->__isInitialized__ = true; + $overrideLocalValues = true; + + if ($entity instanceof NotifyPropertyChanged) { + $entity->addPropertyChangedListener($this); + } + } else { + $overrideLocalValues = isset($hints[Query::HINT_REFRESH]); + + // If only a specific entity is set to refresh, check that it's the one + if(isset($hints[Query::HINT_REFRESH_ENTITY])) { + $overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity; + + // inject ObjectManager into just loaded proxies. + if ($overrideLocalValues && $entity instanceof ObjectManagerAware) { + $entity->injectObjectManager($this->em, $class); + } + } + } + + if ($overrideLocalValues) { + $this->originalEntityData[$oid] = $data; + } + } else { + $entity = $this->newInstance($class); + $oid = spl_object_hash($entity); + + $this->entityIdentifiers[$oid] = $id; + $this->entityStates[$oid] = self::STATE_MANAGED; + $this->originalEntityData[$oid] = $data; + $this->identityMap[$class->rootEntityName][$idHash] = $entity; + + if ($entity instanceof NotifyPropertyChanged) { + $entity->addPropertyChangedListener($this); + } + + $overrideLocalValues = true; + } + + if ( ! $overrideLocalValues) { + return $entity; + } + + foreach ($data as $field => $value) { + if (isset($class->fieldMappings[$field])) { + $class->reflFields[$field]->setValue($entity, $value); + } + } + + // Loading the entity right here, if its in the eager loading map get rid of it there. + unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]); + + if (isset($this->eagerLoadingEntities[$class->rootEntityName]) && ! $this->eagerLoadingEntities[$class->rootEntityName]) { + unset($this->eagerLoadingEntities[$class->rootEntityName]); + } + + // Properly initialize any unfetched associations, if partial objects are not allowed. + if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) { + return $entity; + } + + foreach ($class->associationMappings as $field => $assoc) { + // Check if the association is not among the fetch-joined associations already. + if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) { + continue; + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + switch (true) { + case ($assoc['type'] & ClassMetadata::TO_ONE): + if ( ! $assoc['isOwningSide']) { + // Inverse side of x-to-one can never be lazy + $class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity)); + + continue 2; + } + + $associatedId = array(); + + // TODO: Is this even computed right in all cases of composite keys? + foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) { + $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null; + + if ($joinColumnValue !== null) { + if ($targetClass->containsForeignIdentifier) { + $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue; + } else { + $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue; + } + } + } + + if ( ! $associatedId) { + // Foreign key is NULL + $class->reflFields[$field]->setValue($entity, null); + $this->originalEntityData[$oid][$field] = null; + + continue; + } + + if ( ! isset($hints['fetchMode'][$class->name][$field])) { + $hints['fetchMode'][$class->name][$field] = $assoc['fetch']; + } + + // Foreign key is set + // Check identity map first + // FIXME: Can break easily with composite keys if join column values are in + // wrong order. The correct order is the one in ClassMetadata#identifier. + $relatedIdHash = implode(' ', $associatedId); + + switch (true) { + case (isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash])): + $newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash]; + + // if this is an uninitialized proxy, we are deferring eager loads, + // this association is marked as eager fetch, and its an uninitialized proxy (wtf!) + // then we cann append this entity for eager loading! + if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER && + isset($hints['deferEagerLoad']) && + !$targetClass->isIdentifierComposite && + $newValue instanceof Proxy && + $newValue->__isInitialized__ === false) { + + $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId); + } + + break; + + case ($targetClass->subClasses): + // If it might be a subtype, it can not be lazy. There isn't even + // a way to solve this with deferred eager loading, which means putting + // an entity with subclasses at a *-to-one location is really bad! (performance-wise) + $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId); + break; + + default: + switch (true) { + // We are negating the condition here. Other cases will assume it is valid! + case ($hints['fetchMode'][$class->name][$field] !== ClassMetadata::FETCH_EAGER): + $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId); + break; + + // Deferred eager load only works for single identifier classes + case (isset($hints['deferEagerLoad']) && ! $targetClass->isIdentifierComposite): + // TODO: Is there a faster approach? + $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId); + + $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId); + break; + + default: + // TODO: This is very imperformant, ignore it? + $newValue = $this->em->find($assoc['targetEntity'], $associatedId); + break; + } + + // PERF: Inlined & optimized code from UnitOfWork#registerManaged() + $newValueOid = spl_object_hash($newValue); + $this->entityIdentifiers[$newValueOid] = $associatedId; + $this->identityMap[$targetClass->rootEntityName][$relatedIdHash] = $newValue; + $this->entityStates[$newValueOid] = self::STATE_MANAGED; + // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also! + break; + } + + $this->originalEntityData[$oid][$field] = $newValue; + $class->reflFields[$field]->setValue($entity, $newValue); + + if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) { + $inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']]; + $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity); + } + + break; + + default: + // Inject collection + $pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection); + $pColl->setOwner($entity, $assoc); + $pColl->setInitialized(false); + + $reflField = $class->reflFields[$field]; + $reflField->setValue($entity, $pColl); + + if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) { + $this->loadCollection($pColl); + $pColl->takeSnapshot(); + } + + $this->originalEntityData[$oid][$field] = $pColl; + break; + } + } + + if ($overrideLocalValues) { + if (isset($class->lifecycleCallbacks[Events::postLoad])) { + $class->invokeLifecycleCallbacks(Events::postLoad, $entity); + } + + + if ($this->evm->hasListeners(Events::postLoad)) { + $this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->em)); + } + } + + return $entity; + } + + /** + * @return void + */ + public function triggerEagerLoads() + { + if ( ! $this->eagerLoadingEntities) { + return; + } + + // avoid infinite recursion + $eagerLoadingEntities = $this->eagerLoadingEntities; + $this->eagerLoadingEntities = array(); + + foreach ($eagerLoadingEntities as $entityName => $ids) { + $class = $this->em->getClassMetadata($entityName); + + if ($ids) { + $this->getEntityPersister($entityName)->loadAll( + array_combine($class->identifier, array(array_values($ids))) + ); + } + } + } + + /** + * Initializes (loads) an uninitialized persistent collection of an entity. + * + * @param PeristentCollection $collection The collection to initialize. + * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733. + */ + public function loadCollection(PersistentCollection $collection) + { + $assoc = $collection->getMapping(); + $persister = $this->getEntityPersister($assoc['targetEntity']); + + switch ($assoc['type']) { + case ClassMetadata::ONE_TO_MANY: + $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection); + break; + + case ClassMetadata::MANY_TO_MANY: + $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection); + break; + } + } + + /** + * Gets the identity map of the UnitOfWork. + * + * @return array + */ + public function getIdentityMap() + { + return $this->identityMap; + } + + /** + * Gets the original data of an entity. The original data is the data that was + * present at the time the entity was reconstituted from the database. + * + * @param object $entity + * @return array + */ + public function getOriginalEntityData($entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->originalEntityData[$oid])) { + return $this->originalEntityData[$oid]; + } + + return array(); + } + + /** + * @ignore + */ + public function setOriginalEntityData($entity, array $data) + { + $this->originalEntityData[spl_object_hash($entity)] = $data; + } + + /** + * INTERNAL: + * Sets a property value of the original data array of an entity. + * + * @ignore + * @param string $oid + * @param string $property + * @param mixed $value + */ + public function setOriginalEntityProperty($oid, $property, $value) + { + $this->originalEntityData[$oid][$property] = $value; + } + + /** + * Gets the identifier of an entity. + * The returned value is always an array of identifier values. If the entity + * has a composite identifier then the identifier values are in the same + * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames(). + * + * @param object $entity + * @return array The identifier values. + */ + public function getEntityIdentifier($entity) + { + return $this->entityIdentifiers[spl_object_hash($entity)]; + } + + /** + * Tries to find an entity with the given identifier in the identity map of + * this UnitOfWork. + * + * @param mixed $id The entity identifier to look for. + * @param string $rootClassName The name of the root class of the mapped entity hierarchy. + * @return mixed Returns the entity with the specified identifier if it exists in + * this UnitOfWork, FALSE otherwise. + */ + public function tryGetById($id, $rootClassName) + { + $idHash = implode(' ', (array) $id); + + if (isset($this->identityMap[$rootClassName][$idHash])) { + return $this->identityMap[$rootClassName][$idHash]; + } + + return false; + } + + /** + * Schedules an entity for dirty-checking at commit-time. + * + * @param object $entity The entity to schedule for dirty-checking. + * @todo Rename: scheduleForSynchronization + */ + public function scheduleForDirtyCheck($entity) + { + $rootClassName = $this->em->getClassMetadata(get_class($entity))->rootEntityName; + + $this->scheduledForDirtyCheck[$rootClassName][spl_object_hash($entity)] = $entity; + } + + /** + * Checks whether the UnitOfWork has any pending insertions. + * + * @return boolean TRUE if this UnitOfWork has pending insertions, FALSE otherwise. + */ + public function hasPendingInsertions() + { + return ! empty($this->entityInsertions); + } + + /** + * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the + * number of entities in the identity map. + * + * @return integer + */ + public function size() + { + $countArray = array_map(function ($item) { return count($item); }, $this->identityMap); + + return array_sum($countArray); + } + + /** + * Gets the EntityPersister for an Entity. + * + * @param string $entityName The name of the Entity. + * + * @return \Doctrine\ORM\Persisters\BasicEntityPersister + */ + public function getEntityPersister($entityName) + { + if (isset($this->persisters[$entityName])) { + return $this->persisters[$entityName]; + } + + $class = $this->em->getClassMetadata($entityName); + + switch (true) { + case ($class->isInheritanceTypeNone()): + $persister = new Persisters\BasicEntityPersister($this->em, $class); + break; + + case ($class->isInheritanceTypeSingleTable()): + $persister = new Persisters\SingleTablePersister($this->em, $class); + break; + + case ($class->isInheritanceTypeJoined()): + $persister = new Persisters\JoinedSubclassPersister($this->em, $class); + break; + + default: + $persister = new Persisters\UnionSubclassPersister($this->em, $class); + } + + $this->persisters[$entityName] = $persister; + + return $this->persisters[$entityName]; + } + + /** + * Gets a collection persister for a collection-valued association. + * + * @param AssociationMapping $association + * + * @return AbstractCollectionPersister + */ + public function getCollectionPersister(array $association) + { + $type = $association['type']; + + if (isset($this->collectionPersisters[$type])) { + return $this->collectionPersisters[$type]; + } + + switch ($type) { + case ClassMetadata::ONE_TO_MANY: + $persister = new Persisters\OneToManyPersister($this->em); + break; + + case ClassMetadata::MANY_TO_MANY: + $persister = new Persisters\ManyToManyPersister($this->em); + break; + } + + $this->collectionPersisters[$type] = $persister; + + return $this->collectionPersisters[$type]; + } + + /** + * INTERNAL: + * Registers an entity as managed. + * + * @param object $entity The entity. + * @param array $id The identifier values. + * @param array $data The original entity data. + */ + public function registerManaged($entity, array $id, array $data) + { + $oid = spl_object_hash($entity); + + $this->entityIdentifiers[$oid] = $id; + $this->entityStates[$oid] = self::STATE_MANAGED; + $this->originalEntityData[$oid] = $data; + + $this->addToIdentityMap($entity); + } + + /** + * INTERNAL: + * Clears the property changeset of the entity with the given OID. + * + * @param string $oid The entity's OID. + */ + public function clearEntityChangeSet($oid) + { + $this->entityChangeSets[$oid] = array(); + } + + /* PropertyChangedListener implementation */ + + /** + * Notifies this UnitOfWork of a property change in an entity. + * + * @param object $entity The entity that owns the property. + * @param string $propertyName The name of the property that changed. + * @param mixed $oldValue The old value of the property. + * @param mixed $newValue The new value of the property. + */ + public function propertyChanged($entity, $propertyName, $oldValue, $newValue) + { + $oid = spl_object_hash($entity); + $class = $this->em->getClassMetadata(get_class($entity)); + + $isAssocField = isset($class->associationMappings[$propertyName]); + + if ( ! $isAssocField && ! isset($class->fieldMappings[$propertyName])) { + return; // ignore non-persistent fields + } + + // Update changeset and mark entity for synchronization + $this->entityChangeSets[$oid][$propertyName] = array($oldValue, $newValue); + + if ( ! isset($this->scheduledForDirtyCheck[$class->rootEntityName][$oid])) { + $this->scheduleForDirtyCheck($entity); + } + } + + /** + * Gets the currently scheduled entity insertions in this UnitOfWork. + * + * @return array + */ + public function getScheduledEntityInsertions() + { + return $this->entityInsertions; + } + + /** + * Gets the currently scheduled entity updates in this UnitOfWork. + * + * @return array + */ + public function getScheduledEntityUpdates() + { + return $this->entityUpdates; + } + + /** + * Gets the currently scheduled entity deletions in this UnitOfWork. + * + * @return array + */ + public function getScheduledEntityDeletions() + { + return $this->entityDeletions; + } + + /** + * Get the currently scheduled complete collection deletions + * + * @return array + */ + public function getScheduledCollectionDeletions() + { + return $this->collectionDeletions; + } + + /** + * Gets the currently scheduled collection inserts, updates and deletes. + * + * @return array + */ + public function getScheduledCollectionUpdates() + { + return $this->collectionUpdates; + } + + /** + * Helper method to initialize a lazy loading proxy or persistent collection. + * + * @param object + * @return void + */ + public function initializeObject($obj) + { + if ($obj instanceof Proxy) { + $obj->__load(); + + return; + } + + if ($obj instanceof PersistentCollection) { + $obj->initialize(); + } + } + + /** + * Helper method to show an object as string. + * + * @param object $obj + * @return string + */ + private static function objToStr($obj) + { + return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj); + } + + /** + * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit(). + * + * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information + * on this object that might be necessary to perform a correct udpate. + * + * @throws \InvalidArgumentException + * @param $object + * @return void + */ + public function markReadOnly($object) + { + if ( ! is_object($object) || ! $this->isInIdentityMap($object)) { + throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object); + } + + $this->readOnlyObjects[spl_object_hash($object)] = true; + } + + /** + * Is this entity read only? + * + * @throws \InvalidArgumentException + * @param $object + * @return void + */ + public function isReadOnly($object) + { + if ( ! is_object($object) ) { + throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object); + } + + return isset($this->readOnlyObjects[spl_object_hash($object)]); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Version.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Version.php new file mode 100644 index 0000000..020148e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Version.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Class to store and retrieve the version of Doctrine + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Version +{ + /** + * Current Doctrine Version + */ + const VERSION = '2.2.3-DEV'; + + /** + * Compares a Doctrine version with the current one. + * + * @param string $version Doctrine version to compare. + * @return int Returns -1 if older, 0 if it is the same, 1 if version + * passed as argument is newer. + */ + public static function compare($version) + { + $currentVersion = str_replace(' ', '', strtolower(self::VERSION)); + $version = str_replace(' ', '', $version); + + return version_compare($version, $currentVersion); + } +} diff --git a/vendor/doctrine/orm/phpunit.xml.dist b/vendor/doctrine/orm/phpunit.xml.dist new file mode 100644 index 0000000..3ab5edb --- /dev/null +++ b/vendor/doctrine/orm/phpunit.xml.dist @@ -0,0 +1,59 @@ + + + + + + + ./tests/Doctrine/Tests/ORM + + + + + + performance + locking_functional + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/doctrine/orm/run-all.sh b/vendor/doctrine/orm/run-all.sh new file mode 100755 index 0000000..80712ee --- /dev/null +++ b/vendor/doctrine/orm/run-all.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# This script is a small convenience wrapper for running the doctrine testsuite against a large bunch of databases. +# Just create the phpunit.xmls as described in the array below and configure the specific files section +# to connect to that database. Just omit a file if you dont have that database and the tests will be skipped. + +configs[1]="mysql.phpunit.xml" +configs[2]='postgres.phpunit.xml' +configs[3]='sqlite.phpunit.xml' +configs[4]='oracle.phpunit.xml' +configs[5]='db2.phpunit.xml' +configs[6]='pdo-ibm.phpunit.xml' +configs[7]='sqlsrv.phpunit.xml' + +for i in "${configs[@]}"; do + if [ -f "$i" ]; + then + echo "RUNNING TESTS WITH CONFIG $i" + phpunit -c "$i" "$@" + fi; +done diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/Aop/InterceptorLoader.php b/vendor/jms/aop-bundle/JMS/AopBundle/Aop/InterceptorLoader.php new file mode 100644 index 0000000..79ac040 --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/Aop/InterceptorLoader.php @@ -0,0 +1,58 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\AopBundle\Aop; + +use CG\Proxy\InterceptorLoaderInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Lazy-loading interceptor loader implementation. + * + * @author Johannes M. Schmitt + */ +class InterceptorLoader implements InterceptorLoaderInterface +{ + private $container; + private $interceptors; + private $loadedInterceptors = array(); + + public function __construct(ContainerInterface $container, array $interceptors) + { + $this->container = $container; + $this->interceptors = $interceptors; + } + + public function loadInterceptors(\ReflectionMethod $method) + { + if (!isset($this->interceptors[$method->class][$method->name])) { + return array(); + } + + if (isset($this->loadedInterceptors[$method->class][$method->name])) { + return $this->loadedInterceptors[$method->class][$method->name]; + } + + $interceptors = array(); + foreach ($this->interceptors[$method->class][$method->name] as $id) { + $interceptors[] = $this->container->get($id); + } + + return $this->loadedInterceptors[$method->class][$method->name] = $interceptors; + } +} \ No newline at end of file diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/Aop/PointcutContainer.php b/vendor/jms/aop-bundle/JMS/AopBundle/Aop/PointcutContainer.php new file mode 100644 index 0000000..5c73741 --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/Aop/PointcutContainer.php @@ -0,0 +1,34 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\AopBundle\Aop; + +final class PointcutContainer +{ + private $pointcuts; + + public function __construct(array $pointcuts) + { + $this->pointcuts = $pointcuts; + } + + public function getPointcuts() + { + return $this->pointcuts; + } +} \ No newline at end of file diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/Aop/PointcutInterface.php b/vendor/jms/aop-bundle/JMS/AopBundle/Aop/PointcutInterface.php new file mode 100644 index 0000000..a263d52 --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/Aop/PointcutInterface.php @@ -0,0 +1,54 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\AopBundle\Aop; + +/** + * Pointcut Interface. + * + * Implementations of this class are responsible for making a decision on whether + * a certain method call matches the advice which is associated with this pointcut. + * + * @author Johannes M. Schmitt + */ +interface PointcutInterface +{ + /** + * Determines whether the advice applies to instances of the given class. + * + * There are some limits as to what you can do in this method. Namely, you may + * only base your decision on resources that are part of the ContainerBuilder. + * Specifically, you may not use any data in the class itself, such as + * annotations. + * + * @param \ReflectionClass $class + * @return boolean + */ + function matchesClass(\ReflectionClass $class); + + /** + * Determines whether the advice applies to the given method. + * + * This method is not limited in the way the matchesClass method is. It may + * use information in the associated class to make its decision. + * + * @param \ReflectionMethod $method + * @return boolean + */ + function matchesMethod(\ReflectionMethod $method); +} \ No newline at end of file diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/Aop/RegexPointcut.php b/vendor/jms/aop-bundle/JMS/AopBundle/Aop/RegexPointcut.php new file mode 100644 index 0000000..cbb1404 --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/Aop/RegexPointcut.php @@ -0,0 +1,46 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\AopBundle\Aop; + +/** + * A regex pointcut implementation. + * + * Uses a regular expression for determining whether the pointcut matches. + * + * @author Johannes M. Schmitt + */ +class RegexPointcut implements PointcutInterface +{ + private $pattern; + + public function __construct($pattern) + { + $this->pattern = $pattern; + } + + public function matchesClass(\ReflectionClass $class) + { + return true; + } + + public function matchesMethod(\ReflectionMethod $method) + { + return 0 < preg_match('#'.$this->pattern.'#', sprintf('%s::%s', $method->class, $method->name)); + } +} \ No newline at end of file diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/DependencyInjection/Compiler/PointcutMatchingPass.php b/vendor/jms/aop-bundle/JMS/AopBundle/DependencyInjection/Compiler/PointcutMatchingPass.php new file mode 100644 index 0000000..51967a6 --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/DependencyInjection/Compiler/PointcutMatchingPass.php @@ -0,0 +1,195 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\AopBundle\DependencyInjection\Compiler; + +use CG\Core\ClassUtils; + +use JMS\AopBundle\Exception\RuntimeException; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\DependencyInjection\Reference; +use CG\Proxy\Enhancer; +use CG\Proxy\InterceptionGenerator; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Matches pointcuts against service methods. + * + * This pass will collect the advices that match a certain method, and then + * generate proxy classes where necessary. + * + * @author Johannes M. Schmitt + */ +class PointcutMatchingPass implements CompilerPassInterface +{ + private $pointcuts; + private $cacheDir; + private $container; + + public function __construct(array $pointcuts = null) + { + $this->pointcuts = $pointcuts; + } + + public function process(ContainerBuilder $container) + { + $this->container = $container; + $this->cacheDir = $container->getParameter('jms_aop.cache_dir').'/proxies'; + $pointcuts = $this->getPointcuts(); + + $interceptors = array(); + foreach ($container->getDefinitions() as $id => $definition) { + $this->processDefinition($definition, $pointcuts, $interceptors); + + $this->processInlineDefinitions($pointcuts, $interceptors, $definition->getArguments()); + $this->processInlineDefinitions($pointcuts, $interceptors, $definition->getMethodCalls()); + $this->processInlineDefinitions($pointcuts, $interceptors, $definition->getProperties()); + } + + $container + ->getDefinition('jms_aop.interceptor_loader') + ->addArgument($interceptors) + ; + } + + private function processInlineDefinitions($pointcuts, &$interceptors, array $a) { + foreach ($a as $k => $v) { + if ($v instanceof Definition) { + $this->processDefinition($v, $pointcuts, $interceptors); + } else if (is_array($v)) { + $this->processInlineDefinitions($pointcuts, $interceptors, $v); + } + } + } + + private function processDefinition(Definition $definition, $pointcuts, &$interceptors) + { + if ($definition->isSynthetic()) { + return; + } + + if ($definition->getFactoryService() || $definition->getFactoryClass()) { + return; + } + + if ($file = $definition->getFile()) { + require_once $file; + } + + if (!class_exists($definition->getClass())) { + return; + } + + $class = new \ReflectionClass($definition->getClass()); + + // check if class is matched + $matchingPointcuts = array(); + foreach ($pointcuts as $interceptor => $pointcut) { + if ($pointcut->matchesClass($class)) { + $matchingPointcuts[$interceptor] = $pointcut; + } + } + + if (empty($matchingPointcuts)) { + return; + } + + $this->addResources($class, $this->container); + + if ($class->isFinal()) { + return; + } + + $classAdvices = array(); + foreach ($class->getMethods(\ReflectionMethod::IS_PROTECTED | \ReflectionMethod::IS_PUBLIC) as $method) { + if ($method->isFinal()) { + continue; + } + + $advices = array(); + foreach ($matchingPointcuts as $interceptor => $pointcut) { + if ($pointcut->matchesMethod($method)) { + $advices[] = $interceptor; + } + } + + if (empty($advices)) { + continue; + } + + $classAdvices[$method->name] = $advices; + } + + if (empty($classAdvices)) { + return; + } + + $interceptors[ClassUtils::getUserClass($class->name)] = $classAdvices; + + $generator = new InterceptionGenerator(); + $generator->setFilter(function(\ReflectionMethod $method) use ($classAdvices) { + return isset($classAdvices[$method->name]); + }); + if ($file) { + $generator->setRequiredFile($file); + } + $enhancer = new Enhancer($class, array(), array( + $generator + )); + $enhancer->writeClass($filename = $this->cacheDir.'/'.str_replace('\\', '-', $class->name).'.php'); + $definition->setFile($filename); + $definition->setClass($enhancer->getClassName($class)); + $definition->addMethodCall('__CGInterception__setLoader', array( + new Reference('jms_aop.interceptor_loader') + )); + } + + private function addResources(\ReflectionClass $class) + { + do { + $this->container->addResource(new FileResource($class->getFilename())); + } while (($class = $class->getParentClass()) && $class->getFilename()); + } + + private function getPointcuts() + { + if (null !== $this->pointcuts) { + return $this->pointcuts; + } + + $pointcuts = $pointcutReferences = array(); + + foreach ($this->container->findTaggedServiceIds('jms_aop.pointcut') as $id => $attr) { + if (!isset($attr[0]['interceptor'])) { + throw new RuntimeException('You need to set the "interceptor" attribute for the "jms_aop.pointcut" tag of service "'.$id.'".'); + } + + $pointcutReferences[$attr[0]['interceptor']] = new Reference($id); + $pointcuts[$attr[0]['interceptor']] = $this->container->get($id); + } + + $this->container + ->getDefinition('jms_aop.pointcut_container') + ->addArgument($pointcutReferences) + ; + + return $pointcuts; + } +} diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/DependencyInjection/Configuration.php b/vendor/jms/aop-bundle/JMS/AopBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..947cdf5 --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/DependencyInjection/Configuration.php @@ -0,0 +1,45 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\AopBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * This is the class that validates and merges configuration from your app/config files + * + * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class} + */ +class Configuration implements ConfigurationInterface +{ + /** + * {@inheritDoc} + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $treeBuilder->root('jms_aop') + ->children() + ->scalarNode('cache_dir')->cannotBeEmpty()->defaultValue('%kernel.cache_dir%/jms_aop')->end() + ->end() + ; + + return $treeBuilder; + } +} diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/DependencyInjection/JMSAopExtension.php b/vendor/jms/aop-bundle/JMS/AopBundle/DependencyInjection/JMSAopExtension.php new file mode 100644 index 0000000..6f9a1e3 --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/DependencyInjection/JMSAopExtension.php @@ -0,0 +1,53 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\AopBundle\DependencyInjection; + +use JMS\AopBundle\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Loader; + +/** + * JMSAopExtension. + * + * @author Johannes M. Schmitt + */ +class JMSAopExtension extends Extension +{ + /** + * {@inheritDoc} + */ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = new Configuration(); + $config = $this->processConfiguration($configuration, $configs); + + $cacheDir = $container->getParameterBag()->resolveValue($config['cache_dir']); + if (!is_dir($cacheDir)) { + if (false === @mkdir($cacheDir, 0777, true)) { + throw new RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir)); + } + } + $container->setParameter('jms_aop.cache_dir', $cacheDir); + + $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.xml'); + } +} diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/Exception/Exception.php b/vendor/jms/aop-bundle/JMS/AopBundle/Exception/Exception.php new file mode 100644 index 0000000..1cbc27e --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/Exception/Exception.php @@ -0,0 +1,28 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\AopBundle\Exception; + +/** + * Base exception for the AopBundle. + * + * @author Johannes M. Schmitt + */ +interface Exception +{ +} \ No newline at end of file diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/Exception/RuntimeException.php b/vendor/jms/aop-bundle/JMS/AopBundle/Exception/RuntimeException.php new file mode 100644 index 0000000..02a0db4 --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/Exception/RuntimeException.php @@ -0,0 +1,28 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\AopBundle\Exception; + +/** + * RuntimeException for the AopBundle. + * + * @author Johannes M. Schmitt + */ +class RuntimeException extends \RuntimeException implements Exception +{ +} \ No newline at end of file diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/JMSAopBundle.php b/vendor/jms/aop-bundle/JMS/AopBundle/JMSAopBundle.php new file mode 100644 index 0000000..dbd6b8b --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/JMSAopBundle.php @@ -0,0 +1,34 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\AopBundle; + +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use JMS\AopBundle\DependencyInjection\Compiler\PointcutMatchingPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class JMSAopBundle extends Bundle +{ + const VERSION = '1.0.0'; + + public function build(ContainerBuilder $container) + { + $container->addCompilerPass(new PointcutMatchingPass(), PassConfig::TYPE_AFTER_REMOVING); + } +} diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/README b/vendor/jms/aop-bundle/JMS/AopBundle/README new file mode 100644 index 0000000..72bab21 --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/README @@ -0,0 +1,8 @@ +For documentation, see: + + Resources/doc + + +For license, see: + + Resources/meta/LICENSE \ No newline at end of file diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/Resources/config/services.xml b/vendor/jms/aop-bundle/JMS/AopBundle/Resources/config/services.xml new file mode 100644 index 0000000..c1caed4 --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/Resources/config/services.xml @@ -0,0 +1,18 @@ + + + + + + JMS\AopBundle\Aop\InterceptorLoader + + + + + + + + + + diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/Resources/doc/index.rst b/vendor/jms/aop-bundle/JMS/AopBundle/Resources/doc/index.rst new file mode 100644 index 0000000..e637fb0 --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/Resources/doc/index.rst @@ -0,0 +1,252 @@ +======== +Overview +======== + +This bundle adds AOP capabilities to Symfony2. + +If you haven't heard of AOP yet, it basically allows you to separate a +cross-cutting concern (for example, security checks) into a dedicated class, +and not having to repeat that code in all places where it is needed. + +In other words, this allows you to execute custom code before, and after the +invocation of certain methods in your service layer, or your controllers. You +can also choose to skip the invocation of the original method, or throw exceptions. + +Installation +------------ +Checkout a copy of the code:: + + git submodule add https://github.com/schmittjoh/JMSAopBundle.git src/JMS/AopBundle + +Then register the bundle with your kernel:: + + // in AppKernel::registerBundles() + $bundles = array( + // ... + new JMS\AopBundle\JMSAopBundle(), + // ... + ); + +This bundle also requires the CG library for code generation:: + + git submodule add https://github.com/schmittjoh/cg-library.git vendor/cg-library + +Make sure that you also register the namespaces with the autoloader:: + + // app/autoload.php + $loader->registerNamespaces(array( + // ... + 'JMS' => __DIR__.'/../vendor/bundles', + 'CG' => __DIR__.'/../vendor/cg-library/src', + // ... + )); + + +Configuration +------------- +:: + + jms_aop: + cache_dir: %kernel.cache_dir%/jms_aop + + +Usage +----- +In order to execute custom code, you need two classes. First, you need a so-called +pointcut. The purpose of this class is to make a decision whether a method call +should be intercepted by a certain interceptor. This decision has to be made +statically only on the basis of the method signature itself. + +The second class is the interceptor. This class is being called instead +of the original method. It contains the custom code that you would like to +execute. At this point, you have access to the object on which the method is +called, and all the arguments which were passed to that method. + +Examples +-------- + +1. Logging +~~~~~~~~~~ + +In this example, we will be implementing logging for all methods that contain +"delete". + +Pointcut +^^^^^^^^ + +:: + + name, 'delete'); + } + } + +:: + + # services.yml + services: + my_logging_pointcut: + class: LoggingPointcut + tags: + - { name: jms_aop.pointcut, interceptor: logging_interceptor } + + +LoggingInterceptor +^^^^^^^^^^^^^^^^^^ + +:: + + context = $context; + $this->logger = $logger; + } + + public function intercept(MethodInvocation $invocation) + { + $user = $this->context->getToken()->getUsername(); + $this->logger->info(sprintf('User "%s" invoked method "%s".', $user, $invocation->reflection->name)); + + // make sure to proceed with the invocation otherwise the original + // method will never be called + return $invocation->proceed(); + } + } + +:: + + # services.yml + services: + logging_interceptor: + class: LoggingInterceptor + arguments: [@security.context, @logger] + + +2. Transaction Management +~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this example, we add a @Transactional annotation, and we automatically wrap all methods +where this annotation is declared in a transaction. + +Pointcut +^^^^^^^^ + +:: + + use Doctrine\Common\Annotations\Reader; + use JMS\AopBundle\Aop\PointcutInterface; + use JMS\DiExtraBundle\Annotation as DI; + + /** + * @DI\Service + * @DI\Tag("jms_aop.pointcut", attributes = {"interceptor" = "aop.transactional_interceptor"}) + * + * @author Johannes M. Schmitt + */ + class TransactionalPointcut implements PointcutInterface + { + private $reader; + + /** + * @DI\InjectParams({ + * "reader" = @DI\Inject("annotation_reader"), + * }) + * @param Reader $reader + */ + public function __construct(Reader $reader) + { + $this->reader = $reader; + } + + public function matchesClass(\ReflectionClass $class) + { + return true; + } + + public function matchesMethod(\ReflectionMethod $method) + { + return null !== $this->reader->getMethodAnnotation($method, 'Annotation\Transactional'); + } + } + +Interceptor +^^^^^^^^^^^ + +:: + + use Symfony\Component\HttpKernel\Log\LoggerInterface; + use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + use CG\Proxy\MethodInvocation; + use CG\Proxy\MethodInterceptorInterface; + use Doctrine\ORM\EntityManager; + use JMS\DiExtraBundle\Annotation as DI; + + /** + * @DI\Service("aop.transactional_interceptor") + * + * @author Johannes M. Schmitt + */ + class TransactionalInterceptor implements MethodInterceptorInterface + { + private $em; + private $logger; + + /** + * @DI\InjectParams + * @param EntityManager $em + */ + public function __construct(EntityManager $em, LoggerInterface $logger) + { + $this->em = $em; + $this->logger = $logger; + } + + public function intercept(MethodInvocation $invocation) + { + $this->logger->info('Beginning transaction for method "'.$invocation.'")'); + $this->em->getConnection()->beginTransaction(); + try { + $rs = $invocation->proceed(); + + $this->logger->info(sprintf('Comitting transaction for method "%s" (method invocation successful)', $invocation)); + $this->em->getConnection()->commit(); + + return $rs; + } catch (\Exception $ex) { + if ($ex instanceof NotFoundHttpException) { + $this->logger->info(sprintf('Committing transaction for method "%s" (exception thrown, but no rollback)', $invocation)); + $this->em->getConnection()->commit(); + } else { + $this->logger->info(sprintf('Rolling back transaction for method "%s" (exception thrown)', $invocation)); + $this->em->getConnection()->rollBack(); + } + + throw $ex; + } + } + } diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/Resources/meta/LICENSE b/vendor/jms/aop-bundle/JMS/AopBundle/Resources/meta/LICENSE new file mode 100644 index 0000000..753842b --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/Resources/meta/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/vendor/jms/aop-bundle/JMS/AopBundle/composer.json b/vendor/jms/aop-bundle/JMS/AopBundle/composer.json new file mode 100644 index 0000000..1fe7e80 --- /dev/null +++ b/vendor/jms/aop-bundle/JMS/AopBundle/composer.json @@ -0,0 +1,21 @@ +{ + "name": "jms/aop-bundle", + "description": "Adds AOP capabilities to Symfony2", + "keywords": ["annotations","aop"], + "type": "symfony-bundle", + "license": "Apache", + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "require": { + "symfony/framework-bundle": "2.*", + "jms/cg": "1.0.0" + }, + "autoload": { + "psr-0": { "JMS\\AopBundle": "" } + }, + "target-dir": "JMS/AopBundle" +} diff --git a/vendor/jms/cg/LICENSE b/vendor/jms/cg/LICENSE new file mode 100644 index 0000000..f49a4e1 --- /dev/null +++ b/vendor/jms/cg/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/vendor/jms/cg/composer.json b/vendor/jms/cg/composer.json new file mode 100644 index 0000000..edb5eec --- /dev/null +++ b/vendor/jms/cg/composer.json @@ -0,0 +1,19 @@ +{ + "name": "jms/cg", + "description": "Toolset for generating PHP code", + "keywords": ["code generation"], + "type": "library", + "license": "Apache", + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-0": { "CG\\": "src/" } + } +} diff --git a/vendor/jms/cg/src/CG/Core/AbstractClassGenerator.php b/vendor/jms/cg/src/CG/Core/AbstractClassGenerator.php new file mode 100644 index 0000000..203d9ba --- /dev/null +++ b/vendor/jms/cg/src/CG/Core/AbstractClassGenerator.php @@ -0,0 +1,60 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Core; + +use CG\Generator\PhpClass; + +/** + * Abstract base class for all class generators. + * + * @author Johannes M. Schmitt + */ +abstract class AbstractClassGenerator implements ClassGeneratorInterface +{ + private $namingStrategy; + private $generatorStrategy; + + public function setNamingStrategy(NamingStrategyInterface $namingStrategy = null) + { + $this->namingStrategy = $namingStrategy; + } + + public function setGeneratorStrategy(GeneratorStrategyInterface $generatorStrategy = null) + { + $this->generatorStrategy = $generatorStrategy; + } + + public function getClassName(\ReflectionClass $class) + { + if (null === $this->namingStrategy) { + $this->namingStrategy = new DefaultNamingStrategy(); + } + + return $this->namingStrategy->getClassName($class); + } + + protected function generateCode(PhpClass $class) + { + if (null === $this->generatorStrategy) { + $this->generatorStrategy = new DefaultGeneratorStrategy(); + } + + return $this->generatorStrategy->generate($class); + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Core/ClassGeneratorInterface.php b/vendor/jms/cg/src/CG/Core/ClassGeneratorInterface.php new file mode 100644 index 0000000..93ca18f --- /dev/null +++ b/vendor/jms/cg/src/CG/Core/ClassGeneratorInterface.php @@ -0,0 +1,36 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Core; + +use CG\Generator\PhpClass; + +/** + * Interface for class generators. + * + * @author Johannes M. Schmitt + */ +interface ClassGeneratorInterface +{ + /** + * Generates the PHP class. + * + * @return string + */ + function generateClass(); +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Core/ClassUtils.php b/vendor/jms/cg/src/CG/Core/ClassUtils.php new file mode 100644 index 0000000..3e33bd5 --- /dev/null +++ b/vendor/jms/cg/src/CG/Core/ClassUtils.php @@ -0,0 +1,35 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Core; + +use CG\Proxy\Enhancer; + +abstract class ClassUtils +{ + public static function getUserClass($className) + { + if (false === $pos = strrpos($className, '\\'.NamingStrategyInterface::SEPARATOR.'\\')) { + return $className; + } + + return substr($className, $pos + NamingStrategyInterface::SEPARATOR_LENGTH + 2); + } + + private final function __construct() {} +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Core/DefaultGeneratorStrategy.php b/vendor/jms/cg/src/CG/Core/DefaultGeneratorStrategy.php new file mode 100644 index 0000000..28b60bf --- /dev/null +++ b/vendor/jms/cg/src/CG/Core/DefaultGeneratorStrategy.php @@ -0,0 +1,67 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Core; + +use CG\Generator\DefaultVisitorInterface; +use CG\Generator\PhpClass; +use CG\Generator\DefaultVisitor; +use CG\Generator\DefaultNavigator; + +/** + * The default generator strategy. + * + * This strategy allows to change the order in which methods, properties and + * constants are sorted. + * + * @author Johannes M. Schmitt + */ +class DefaultGeneratorStrategy implements GeneratorStrategyInterface +{ + private $navigator; + private $visitor; + + public function __construct(DefaultVisitorInterface $visitor = null) + { + $this->navigator = new DefaultNavigator(); + $this->visitor = $visitor ?: new DefaultVisitor(); + } + + public function setConstantSortFunc(\Closure $func = null) + { + $this->navigator->setConstantSortFunc($func); + } + + public function setMethodSortFunc(\Closure $func = null) + { + $this->navigator->setMethodSortFunc($func); + } + + public function setPropertySortFunc(\Closure $func = null) + { + $this->navigator->setPropertySortFunc($func); + } + + public function generate(PhpClass $class) + { + $this->visitor->reset(); + $this->navigator->accept($this->visitor, $class); + + return $this->visitor->getContent(); + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Core/DefaultNamingStrategy.php b/vendor/jms/cg/src/CG/Core/DefaultNamingStrategy.php new file mode 100644 index 0000000..600c995 --- /dev/null +++ b/vendor/jms/cg/src/CG/Core/DefaultNamingStrategy.php @@ -0,0 +1,41 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Core; + +/** + * The default naming strategy. + * + * @author Johannes M. Schmitt + */ +class DefaultNamingStrategy implements NamingStrategyInterface +{ + private $prefix; + + public function __construct($prefix = 'EnhancedProxy') + { + $this->prefix = $prefix; + } + + public function getClassName(\ReflectionClass $class) + { + $userClass = ClassUtils::getUserClass($class->name); + + return $this->prefix.'_'.sha1($class->name).'\\'.self::SEPARATOR.'\\'.$userClass; + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Core/GeneratorStrategyInterface.php b/vendor/jms/cg/src/CG/Core/GeneratorStrategyInterface.php new file mode 100644 index 0000000..2468bc1 --- /dev/null +++ b/vendor/jms/cg/src/CG/Core/GeneratorStrategyInterface.php @@ -0,0 +1,34 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Core; + +use CG\Generator\PhpClass; + +/** + * Generator Strategy Interface. + * + * Implementing classes are responsible for generating PHP code from the given + * PhpClass instance. + * + * @author Johannes M. Schmitt + */ +interface GeneratorStrategyInterface +{ + function generate(PhpClass $class); +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Core/NamingStrategyInterface.php b/vendor/jms/cg/src/CG/Core/NamingStrategyInterface.php new file mode 100644 index 0000000..c7c03fa --- /dev/null +++ b/vendor/jms/cg/src/CG/Core/NamingStrategyInterface.php @@ -0,0 +1,50 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Core; + +/** + * The naming strategy interface. + * + * @author Johannes M. Schmitt + */ +interface NamingStrategyInterface +{ + const SEPARATOR = '__CG__'; + const SEPARATOR_LENGTH = 6; + + /** + * Returns the class name for the proxy class. + * + * The generated class name MUST be the concatenation of a nonempty prefix, + * the namespace separator __CG__, and the original class name. + * + * Examples: + * + * +----------------------------+------------------------------+ + * | Original Name | Generated Name | + * +============================+==============================+ + * | Foo\Bar | dred332\__CG__\Foo\Bar | + * | Bar\Baz | Foo\Doo\__CG__\Bar\Baz | + * +----------------------------+------------------------------+ + * + * @param \ReflectionClass $class + * @return string the class name for the generated class + */ + function getClassName(\ReflectionClass $class); +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Core/ReflectionUtils.php b/vendor/jms/cg/src/CG/Core/ReflectionUtils.php new file mode 100644 index 0000000..e7e9ea7 --- /dev/null +++ b/vendor/jms/cg/src/CG/Core/ReflectionUtils.php @@ -0,0 +1,57 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Core; + +abstract class ReflectionUtils +{ + public static function getOverrideableMethods(\ReflectionClass $class, $publicOnly = false) + { + $filter = \ReflectionMethod::IS_PUBLIC; + + if (!$publicOnly) { + $filter |= \ReflectionMethod::IS_PROTECTED; + } + + return array_filter( + $class->getMethods($filter), + function($method) { return !$method->isFinal() && !$method->isStatic(); } + ); + } + + public static function getUnindentedDocComment($docComment) + { + $lines = explode("\n", $docComment); + for ($i=0,$c=count($lines); $i<$c; $i++) { + if (0 === $i) { + $docBlock = $lines[0]."\n"; + continue; + } + + $docBlock .= ' '.ltrim($lines[$i]); + + if ($i+1 < $c) { + $docBlock .= "\n"; + } + } + + return $docBlock; + } + + private final function __construct() { } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Generator/AbstractPhpMember.php b/vendor/jms/cg/src/CG/Generator/AbstractPhpMember.php new file mode 100644 index 0000000..5ae8aa4 --- /dev/null +++ b/vendor/jms/cg/src/CG/Generator/AbstractPhpMember.php @@ -0,0 +1,95 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Generator; + +/** + * Abstract PHP member class. + * + * @author Johannes M. Schmitt + */ +abstract class AbstractPhpMember +{ + const VISIBILITY_PRIVATE = 'private'; + const VISIBILITY_PROTECTED = 'protected'; + const VISIBILITY_PUBLIC = 'public'; + + private $static = false; + private $visibility = self::VISIBILITY_PUBLIC; + private $name; + private $docblock; + + public function __construct($name = null) + { + $this->setName($name); + } + + public function setName($name) + { + $this->name = $name; + + return $this; + } + + public function setVisibility($visibility) + { + if ($visibility !== self::VISIBILITY_PRIVATE + && $visibility !== self::VISIBILITY_PROTECTED + && $visibility !== self::VISIBILITY_PUBLIC) { + throw new \InvalidArgumentException(sprintf('The visibility "%s" does not exist.', $visibility)); + } + + $this->visibility = $visibility; + + return $this; + } + + public function setStatic($bool) + { + $this->static = (Boolean) $bool; + + return $this; + } + + public function setDocblock($doc) + { + $this->docblock = $doc; + + return $this; + } + + public function isStatic() + { + return $this->static; + } + + public function getVisibility() + { + return $this->visibility; + } + + public function getName() + { + return $this->name; + } + + public function getDocblock() + { + return $this->docblock; + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Generator/DefaultNavigator.php b/vendor/jms/cg/src/CG/Generator/DefaultNavigator.php new file mode 100644 index 0000000..3d46bb3 --- /dev/null +++ b/vendor/jms/cg/src/CG/Generator/DefaultNavigator.php @@ -0,0 +1,172 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Generator; + +/** + * The default navigator. + * + * This class is responsible for the default traversal algorithm of the different + * code elements. + * + * Unlike other visitor pattern implementations, this allows to separate the + * traversal logic from the objects that are traversed. + * + * @author Johannes M. Schmitt + */ +class DefaultNavigator +{ + private $constantSortFunc; + private $propertySortFunc; + private $methodSortFunc; + + /** + * Sets a custom constant sorting function. + * + * @param \Closure $func + */ + public function setConstantSortFunc(\Closure $func = null) + { + $this->constantSortFunc = $func; + } + + /** + * Sets a custom property sorting function. + * + * @param \Closure $func + */ + public function setPropertySortFunc(\Closure $func = null) + { + $this->propertySortFunc = $func; + } + + /** + * Sets a custom method sorting function. + * + * @param \Closure $func + */ + public function setMethodSortFunc(\Closure $func = null) + { + $this->methodSortFunc = $func; + } + + public function accept(DefaultVisitorInterface $visitor, PhpClass $class) + { + $visitor->startVisitingClass($class); + + $constants = $class->getConstants(); + if (!empty($constants)) { + uksort($constants, $this->getConstantSortFunc()); + + $visitor->startVisitingConstants(); + foreach ($constants as $name => $value) { + $visitor->visitConstant($name, $value); + } + $visitor->endVisitingConstants(); + } + + $properties = $class->getProperties(); + if (!empty($properties)) { + usort($properties, $this->getPropertySortFunc()); + + $visitor->startVisitingProperties(); + foreach ($properties as $property) { + $visitor->visitProperty($property); + } + $visitor->endVisitingProperties(); + } + + $methods = $class->getMethods(); + if (!empty($methods)) { + usort($methods, $this->getMethodSortFunc()); + + $visitor->startVisitingMethods(); + foreach ($methods as $method) { + $visitor->visitMethod($method); + } + $visitor->endVisitingMethods(); + } + + $visitor->endVisitingClass($class); + } + + private function getConstantSortFunc() + { + return $this->constantSortFunc ?: 'strcasecmp'; + } + + private function getMethodSortFunc() + { + if (null !== $this->methodSortFunc) { + return $this->methodSortFunc; + } + + static $defaultSortFunc; + if (empty($defaultSortFunc)) { + $defaultSortFunc = function($a, $b) { + if ($a->isStatic() !== $isStatic = $b->isStatic()) { + return $isStatic ? 1 : -1; + } + + if (($aV = $a->getVisibility()) !== $bV = $b->getVisibility()) { + $aV = 'public' === $aV ? 3 : ('protected' === $aV ? 2 : 1); + $bV = 'public' === $bV ? 3 : ('protected' === $bV ? 2 : 1); + + return $aV > $bV ? -1 : 1; + } + + $rs = strcasecmp($a->getName(), $b->getName()); + if (0 === $rs) { + return 0; + } + + return $rs > 0 ? -1 : 1; + }; + } + + return $defaultSortFunc; + } + + private function getPropertySortFunc() + { + if (null !== $this->propertySortFunc) { + return $this->propertySortFunc; + } + + static $defaultSortFunc; + if (empty($defaultSortFunc)) { + $defaultSortFunc = function($a, $b) { + if (($aV = $a->getVisibility()) !== $bV = $b->getVisibility()) { + $aV = 'public' === $aV ? 3 : ('protected' === $aV ? 2 : 1); + $bV = 'public' === $bV ? 3 : ('protected' === $bV ? 2 : 1); + + return $aV > $bV ? -1 : 1; + } + + $rs = strcasecmp($a->getName(), $b->getName()); + if (0 === $rs) { + return 0; + } + + return $rs > 0 ? -1 : 1; + }; + } + + return $defaultSortFunc; + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Generator/DefaultVisitor.php b/vendor/jms/cg/src/CG/Generator/DefaultVisitor.php new file mode 100644 index 0000000..5dd4094 --- /dev/null +++ b/vendor/jms/cg/src/CG/Generator/DefaultVisitor.php @@ -0,0 +1,237 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Generator; + +/** + * The default code generation visitor. + * + * @author Johannes M. Schmitt + */ +class DefaultVisitor implements DefaultVisitorInterface +{ + private $writer; + + public function __construct() + { + $this->writer = new Writer(); + } + + public function reset() + { + $this->writer->reset(); + } + + public function startVisitingClass(PhpClass $class) + { + if ($namespace = $class->getNamespace()) { + $this->writer->write('namespace '.$namespace.';'."\n\n"); + } + + if ($files = $class->getRequiredFiles()) { + foreach ($files as $file) { + $this->writer->writeln('require_once '.var_export($file, true).';'); + } + + $this->writer->write("\n"); + } + + if ($useStatements = $class->getUseStatements()) { + foreach ($useStatements as $alias => $namespace) { + $this->writer->write('use '.$namespace); + + if (substr($namespace, strrpos($namespace, '\\') + 1) !== $alias) { + $this->writer->write(' as '.$alias); + } + + $this->writer->write(";\n"); + } + + $this->writer->write("\n"); + } + + if ($docblock = $class->getDocblock()) { + $this->writer->write($docblock); + } + + $this->writer->write('class '.$class->getShortName()); + + if ($parentClassName = $class->getParentClassName()) { + $this->writer->write(' extends '.('\\' === $parentClassName[0] ? $parentClassName : '\\'.$parentClassName)); + } + + $interfaceNames = $class->getInterfaceNames(); + if (!empty($interfaceNames)) { + $interfaceNames = array_unique($interfaceNames); + + $interfaceNames = array_map(function($name) { + if ('\\' === $name[0]) { + return $name; + } + + return '\\'.$name; + }, $interfaceNames); + + $this->writer->write(' implements '.implode(', ', $interfaceNames)); + } + + $this->writer + ->write("\n{\n") + ->indent() + ; + } + + public function startVisitingConstants() + { + } + + public function visitConstant($name, $value) + { + $this->writer->writeln('const '.$name.' = '.var_export($value, true).';'); + } + + public function endVisitingConstants() + { + $this->writer->write("\n"); + } + + public function startVisitingProperties() + { + } + + public function visitProperty(PhpProperty $property) + { + $this->writer->write($property->getVisibility().' '.($property->isStatic()? 'static ' : '').'$'.$property->getName()); + + if ($property->hasDefaultValue()) { + $this->writer->write(' = '.var_export($property->getDefaultValue(), true)); + } + + $this->writer->writeln(';'); + } + + public function endVisitingProperties() + { + $this->writer->write("\n"); + } + + public function startVisitingMethods() + { + } + + public function visitMethod(PhpMethod $method) + { + if ($docblock = $method->getDocblock()) { + $this->writer->writeln($docblock)->rtrim(); + } + + if ($method->isAbstract()) { + $this->writer->write('abstract '); + } + + $this->writer->write($method->getVisibility().' '); + + if ($method->isStatic()) { + $this->writer->write('static '); + } + + $this->writer->write('function '.$method->getName().'('); + + $this->writeParameters($method->getParameters()); + + if ($method->isAbstract()) { + $this->writer->write(");\n\n"); + + return; + } + + $this->writer + ->writeln(")") + ->writeln('{') + ->indent() + ->writeln($method->getBody()) + ->outdent() + ->rtrim() + ->write("}\n\n") + ; + } + + public function endVisitingMethods() + { + } + + public function endVisitingClass(PhpClass $class) + { + $this->writer + ->outdent() + ->rtrim() + ->write('}') + ; + } + + public function visitFunction(PhpFunction $function) + { + if ($namespace = $function->getNamespace()) { + $this->writer->write("namespace $namespace;\n\n"); + } + + $this->writer->write("function {$function->getName()}("); + $this->writeParameters($function->getParameters()); + $this->writer + ->write(")\n{\n") + ->indent() + ->writeln($function->getBody()) + ->outdent() + ->rtrim() + ->write('}') + ; + } + + public function getContent() + { + return $this->writer->getContent(); + } + + private function writeParameters(array $parameters) + { + $first = true; + foreach ($parameters as $parameter) { + if (!$first) { + $this->writer->write(', '); + } + $first = false; + + if ($type = $parameter->getType()) { + $this->writer->write( + ('array' === $type ? 'array' : ('\\' === $type[0] ? $type : '\\'. $type)) + .' ' + ); + } + + if ($parameter->isPassedByReference()) { + $this->writer->write('&'); + } + + $this->writer->write('$'.$parameter->getName()); + + if ($parameter->hasDefaultValue()) { + $this->writer->write(' = '.var_export($parameter->getDefaultValue(), true)); + } + } + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Generator/DefaultVisitorInterface.php b/vendor/jms/cg/src/CG/Generator/DefaultVisitorInterface.php new file mode 100644 index 0000000..d9532bb --- /dev/null +++ b/vendor/jms/cg/src/CG/Generator/DefaultVisitorInterface.php @@ -0,0 +1,47 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Generator; + +/** + * The visitor interface required by the DefaultNavigator. + * + * @author Johannes M. Schmitt + */ +interface DefaultVisitorInterface +{ + /** + * Resets the visitors internal state to allow re-using the same instance. + * + * @return void + */ + function reset(); + + function startVisitingClass(PhpClass $class); + function startVisitingConstants(); + function visitConstant($name, $value); + function endVisitingConstants(); + function startVisitingProperties(); + function visitProperty(PhpProperty $property); + function endVisitingProperties(); + function startVisitingMethods(); + function visitMethod(PhpMethod $method); + function endVisitingMethods(); + function endVisitingClass(PhpClass $class); + function visitFunction(PhpFunction $function); +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Generator/GeneratorUtils.php b/vendor/jms/cg/src/CG/Generator/GeneratorUtils.php new file mode 100644 index 0000000..8f7a44c --- /dev/null +++ b/vendor/jms/cg/src/CG/Generator/GeneratorUtils.php @@ -0,0 +1,38 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Generator; + +/** + * Some Generator utils. + * + * @author Johannes M. Schmitt + */ +abstract class GeneratorUtils +{ + private final function __construct() {} + + public static function callMethod(\ReflectionMethod $method, array $params = null) + { + if (null === $params) { + $params = array_map(function($p) { return '$'.$p->name; }, $method->getParameters()); + } + + return '\\'.$method->getDeclaringClass()->name.'::'.$method->name.'('.implode(', ', $params).')'; + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Generator/PhpClass.php b/vendor/jms/cg/src/CG/Generator/PhpClass.php new file mode 100644 index 0000000..e8496f0 --- /dev/null +++ b/vendor/jms/cg/src/CG/Generator/PhpClass.php @@ -0,0 +1,360 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Generator; + +use Doctrine\Common\Annotations\PhpParser; + +use CG\Core\ReflectionUtils; + +/** + * Represents a PHP class. + * + * @author Johannes M. Schmitt + */ +class PhpClass +{ + private static $phpParser; + + private $name; + private $parentClassName; + private $interfaceNames = array(); + private $useStatements = array(); + private $constants = array(); + private $properties = array(); + private $requiredFiles = array(); + private $methods = array(); + private $abstract = false; + private $final = false; + private $docblock; + + public static function create($name = null) + { + return new self($name); + } + + public static function fromReflection(\ReflectionClass $ref) + { + $class = new static(); + $class + ->setName($ref->name) + ->setAbstract($ref->isAbstract()) + ->setFinal($ref->isFinal()) + ->setConstants($ref->getConstants()) + ; + + if (null === self::$phpParser) { + if (!class_exists('Doctrine\Common\Annotations\PhpParser')) { + self::$phpParser = false; + } else { + self::$phpParser = new PhpParser(); + } + } + + if (false !== self::$phpParser) { + $class->setUseStatements(self::$phpParser->parseClass($ref)); + } + + if ($docComment = $ref->getDocComment()) { + $class->setDocblock(ReflectionUtils::getUnindentedDocComment($docComment)); + } + + foreach ($ref->getMethods() as $method) { + $class->setMethod(static::createMethod($method)); + } + + foreach ($ref->getProperties() as $property) { + $class->setProperty(static::createProperty($property)); + } + + return $class; + } + + protected static function createMethod(\ReflectionMethod $method) + { + return PhpMethod::fromReflection($method); + } + + protected static function createProperty(\ReflectionProperty $property) + { + return PhpProperty::fromReflection($property); + } + + public function __construct($name = null) + { + $this->name = $name; + } + + public function setName($name) + { + $this->name = $name; + + return $this; + } + + public function setParentClassName($name) + { + $this->parentClassName = $name; + + return $this; + } + + public function setInterfaceNames(array $names) + { + $this->interfaceNames = $names; + + return $this; + } + + public function addInterfaceName($name) + { + $this->interfaceNames[] = $name; + + return $this; + } + + public function setRequiredFiles(array $files) + { + $this->requiredFiles = $files; + + return $this; + } + + public function addRequiredFile($file) + { + $this->requiredFiles[] = $file; + + return $this; + } + + public function setUseStatements(array $useStatements) + { + $this->useStatements = $useStatements; + + return $this; + } + + public function addUseStatement($namespace, $alias = null) + { + if (null === $alias) { + $alias = substr($namespace, strrpos($namespace, '\\') + 1); + } + + $this->useStatements[$alias] = $namespace; + + return $this; + } + + public function setConstants(array $constants) + { + $this->constants = $constants; + + return $this; + } + + public function setConstant($name, $value) + { + $this->constants[$name] = $value; + + return $this; + } + + public function hasConstant($name) + { + return array_key_exists($this->constants, $name); + } + + public function removeConstant($name) + { + if (!array_key_exists($name, $this->constants)) { + throw new \InvalidArgumentException(sprintf('The constant "%s" does not exist.', $name)); + } + + unset($this->constants[$name]); + + return $this; + } + + public function setProperties(array $properties) + { + $this->properties = $properties; + + return $this; + } + + public function setProperty(PhpProperty $property) + { + $this->properties[$property->getName()] = $property; + + return $this; + } + + public function hasProperty($property) + { + if ($property instanceof PhpProperty) { + $property = $property->getName(); + } + + return isset($this->properties[$property]); + } + + public function removeProperty($property) + { + if ($property instanceof PhpProperty) { + $property = $property->getName(); + } + + if (!array_key_exists($property, $this->properties)) { + throw new \InvalidArgumentException(sprintf('The property "%s" does not exist.', $property)); + } + unset($this->properties[$property]); + + return $this; + } + + public function setMethods(array $methods) + { + $this->methods = $methods; + + return $this; + } + + public function setMethod(PhpMethod $method) + { + $this->methods[$method->getName()] = $method; + + return $this; + } + + public function hasMethod($method) + { + if ($method instanceof PhpMethod) { + $method = $method->getName(); + } + + return isset($this->methods[$method]); + } + + public function removeMethod($method) + { + if ($method instanceof PhpMethod) { + $method = $method->getName(); + } + + if (!array_key_exists($method, $this->methods)) { + throw new \InvalidArgumentException(sprintf('The method "%s" does not exist.', $method)); + } + unset($this->methods[$method]); + + return $this; + } + + public function setAbstract($bool) + { + $this->abstract = (Boolean) $bool; + + return $this; + } + + public function setFinal($bool) + { + $this->final = (Boolean) $bool; + + return $this; + } + + public function setDocblock($block) + { + $this->docblock = $block; + + return $this; + } + + public function getName() + { + return $this->name; + } + + public function getParentClassName() + { + return $this->parentClassName; + } + + public function getInterfaceNames() + { + return $this->interfaceNames; + } + + public function getRequiredFiles() + { + return $this->requiredFiles; + } + + public function getUseStatements() + { + return $this->useStatements; + } + + public function getNamespace() + { + if (false === $pos = strrpos($this->name, '\\')) { + return null; + } + + return substr($this->name, 0, $pos); + } + + public function getShortName() + { + if (false === $pos = strrpos($this->name, '\\')) { + return $this->name; + } + + return substr($this->name, $pos+1); + } + + public function getConstants() + { + return $this->constants; + } + + public function getProperties() + { + return $this->properties; + } + + public function getMethods() + { + return $this->methods; + } + + public function isAbstract() + { + return $this->abstract; + } + + public function isFinal() + { + return $this->final; + } + + public function getDocblock() + { + return $this->docblock; + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Generator/PhpFunction.php b/vendor/jms/cg/src/CG/Generator/PhpFunction.php new file mode 100644 index 0000000..2c14511 --- /dev/null +++ b/vendor/jms/cg/src/CG/Generator/PhpFunction.php @@ -0,0 +1,146 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Generator; + +/** + * Represents a PHP function. + * + * @author Johannes M. Schmitt + */ +class PhpFunction +{ + private $name; + private $namespace; + private $parameters = array(); + private $body = ''; + private $referenceReturned = false; + private $docblock; + + public static function create($name = null) + { + return new static($name); + } + + public function __construct($name = null) + { + $this->name = $name; + } + + public function setName($name) + { + $this->name = $name; + + return $this; + } + + public function setNamespace($namespace) + { + $this->namespace = $namespace; + + return $this; + } + + public function setParameters(array $parameters) + { + $this->parameters = $parameters; + + return $this; + } + + public function setReferenceReturned($bool) + { + $this->referenceReturned = (Boolean) $bool; + + return $this; + } + + public function replaceParameter($position, PhpParameter $parameter) + { + if ($position < 0 || $position > count($this->parameters)) { + throw new \InvalidArgumentException(sprintf('$position must be in the range [0, %d].', count($this->parameters))); + } + + $this->parameters[$position] = $parameter; + + return $this; + } + + public function addParameter(PhpParameter $parameter) + { + $this->parameters[] = $parameter; + + return $this; + } + + public function removeParameter($position) + { + if (!isset($this->parameters[$position])) { + throw new \InvalidArgumentException(sprintf('There is not parameter at position %d.', $position)); + } + + unset($this->parameters[$position]); + $this->parameters = array_values($this->parameters); + + return $this; + } + + public function setBody($body) + { + $this->body = $body; + + return $this; + } + + public function setDocblock($docBlock) + { + $this->docblock = $docBlock; + + return $this; + } + + public function getName() + { + return $this->name; + } + + public function getNamespace() + { + return $this->namespace; + } + + public function getParameters() + { + return $this->parameters; + } + + public function getBody() + { + return $this->body; + } + + public function getDocblock() + { + return $this->docblock; + } + + public function isReferenceReturned() + { + return $this->referenceReturned; + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Generator/PhpMethod.php b/vendor/jms/cg/src/CG/Generator/PhpMethod.php new file mode 100644 index 0000000..f8b7314 --- /dev/null +++ b/vendor/jms/cg/src/CG/Generator/PhpMethod.php @@ -0,0 +1,158 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Generator; + +use CG\Core\ReflectionUtils; + +/** + * Represents a PHP method. + * + * @author Johannes M. Schmitt + */ +class PhpMethod extends AbstractPhpMember +{ + private $final = false; + private $abstract = false; + private $parameters = array(); + private $referenceReturned = false; + private $body = ''; + + public static function create($name = null) + { + return new static($name); + } + + public static function fromReflection(\ReflectionMethod $ref) + { + $method = new static(); + $method + ->setFinal($ref->isFinal()) + ->setAbstract($ref->isAbstract()) + ->setStatic($ref->isStatic()) + ->setVisibility($ref->isPublic() ? self::VISIBILITY_PUBLIC : ($ref->isProtected() ? self::VISIBILITY_PROTECTED : self::VISIBILITY_PRIVATE)) + ->setReferenceReturned($ref->returnsReference()) + ->setName($ref->name) + ; + + if ($docComment = $ref->getDocComment()) { + $method->setDocblock(ReflectionUtils::getUnindentedDocComment($docComment)); + } + + foreach ($ref->getParameters() as $param) { + $method->addParameter(static::createParameter($param)); + } + + // FIXME: Extract body? + + return $method; + } + + protected static function createParameter(\ReflectionParameter $parameter) + { + return PhpParameter::fromReflection($parameter); + } + + public function setFinal($bool) + { + $this->final = (Boolean) $bool; + + return $this; + } + + public function setAbstract($bool) + { + $this->abstract = $bool; + + return $this; + } + + public function setReferenceReturned($bool) + { + $this->referenceReturned = (Boolean) $bool; + + return $this; + } + + public function setBody($body) + { + $this->body = $body; + + return $this; + } + + public function setParameters(array $parameters) + { + $this->parameters = array_values($parameters); + + return $this; + } + + public function addParameter(PhpParameter $parameter) + { + $this->parameters[] = $parameter; + + return $this; + } + + public function replaceParameter($position, PhpParameter $parameter) + { + if ($position < 0 || $position > strlen($this->parameters)) { + throw new \InvalidArgumentException(sprintf('The position must be in the range [0, %d].', strlen($this->parameters))); + } + $this->parameters[$position] = $parameter; + + return $this; + } + + public function removeParameter($position) + { + if (!isset($this->parameters[$position])) { + throw new \InvalidArgumentException(sprintf('There is no parameter at position "%d" does not exist.', $position)); + } + unset($this->parameters[$position]); + $this->parameters = array_values($this->parameters); + + return $this; + } + + public function isFinal() + { + return $this->final; + } + + public function isAbstract() + { + return $this->abstract; + } + + public function isReferenceReturned() + { + return $this->referenceReturned; + } + + public function getBody() + { + return $this->body; + } + + public function getParameters() + { + return $this->parameters; + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Generator/PhpParameter.php b/vendor/jms/cg/src/CG/Generator/PhpParameter.php new file mode 100644 index 0000000..0f8b2cb --- /dev/null +++ b/vendor/jms/cg/src/CG/Generator/PhpParameter.php @@ -0,0 +1,126 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Generator; + +/** + * Represents a PHP parameter. + * + * @author Johannes M. Schmitt + */ +class PhpParameter +{ + private $name; + private $defaultValue; + private $hasDefaultValue = false; + private $passedByReference = false; + private $type; + + public static function create($name = null) + { + return new static($name); + } + + public static function fromReflection(\ReflectionParameter $ref) + { + $parameter = new static(); + $parameter + ->setName($ref->name) + ->setPassedByReference($ref->isPassedByReference()) + ; + + if ($ref->isDefaultValueAvailable()) { + $parameter->setDefaultValue($ref->getDefaultValue()); + } + + if ($ref->isArray()) { + $parameter->setType('array'); + } else if ($class = $ref->getClass()) { + $parameter->setType($class->name); + } + + return $parameter; + } + + public function __construct($name = null) + { + $this->name = $name; + } + + public function setName($name) + { + $this->name = $name; + + return $this; + } + + public function setDefaultValue($value) + { + $this->defaultValue = $value; + $this->hasDefaultValue = true; + + return $this; + } + + public function unsetDefaultValue() + { + $this->defaultValue = null; + $this->hasDefaultValue = false; + + return $this; + } + + public function setPassedByReference($bool) + { + $this->passedByReference = (Boolean) $bool; + + return $this; + } + + public function setType($type) + { + $this->type = $type; + + return $this; + } + + public function getName() + { + return $this->name; + } + + public function getDefaultValue() + { + return $this->defaultValue; + } + + public function hasDefaultValue() + { + return $this->hasDefaultValue; + } + + public function isPassedByReference() + { + return $this->passedByReference; + } + + public function getType() + { + return $this->type; + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Generator/PhpProperty.php b/vendor/jms/cg/src/CG/Generator/PhpProperty.php new file mode 100644 index 0000000..a5b33d8 --- /dev/null +++ b/vendor/jms/cg/src/CG/Generator/PhpProperty.php @@ -0,0 +1,84 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Generator; + +use CG\Core\ReflectionUtils; + +/** + * Represents a PHP property. + * + * @author Johannes M. Schmitt + */ +class PhpProperty extends AbstractPhpMember +{ + private $hasDefaultValue = false; + private $defaultValue; + + public static function create($name = null) + { + return new static($name); + } + + public static function fromReflection(\ReflectionProperty $ref) + { + $property = new static(); + $property + ->setName($ref->name) + ->setStatic($ref->isStatic()) + ->setVisibility($ref->isPublic() ? self::VISIBILITY_PUBLIC : ($ref->isProtected() ? self::VISIBILITY_PROTECTED : self::VISIBILITY_PRIVATE)) + ; + + if ($docComment = $ref->getDocComment()) { + $property->setDocblock(ReflectionUtils::getUnindentedDocComment($docComment)); + } + + $defaultProperties = $ref->getDeclaringClass()->getDefaultProperties(); + if (isset($defaultProperties[$ref->name])) { + $property->setDefaultValue($defaultProperties[$ref->name]); + } + + return $property; + } + + public function setDefaultValue($value) + { + $this->defaultValue = $value; + $this->hasDefaultValue = true; + + return $this; + } + + public function unsetDefaultValue() + { + $this->hasDefaultValue = false; + $this->defaultValue = null; + + return $this; + } + + public function hasDefaultValue() + { + return $this->hasDefaultValue; + } + + public function getDefaultValue() + { + return $this->defaultValue; + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Generator/Writer.php b/vendor/jms/cg/src/CG/Generator/Writer.php new file mode 100644 index 0000000..29ca853 --- /dev/null +++ b/vendor/jms/cg/src/CG/Generator/Writer.php @@ -0,0 +1,103 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Generator; + +/** + * A writer implementation. + * + * This may be used to simplify writing well-formatted code. + * + * @author Johannes M. Schmitt + */ +class Writer +{ + private $content = ''; + private $indentationSpaces = 4; + private $indentationLevel = 0; + + public function indent() + { + $this->indentationLevel += 1; + + return $this; + } + + public function outdent() + { + $this->indentationLevel -= 1; + + if ($this->indentationLevel < 0) { + throw new \RuntimeException('The identation level cannot be less than zero.'); + } + + return $this; + } + + public function writeln($content) + { + $this->write($content."\n"); + + return $this; + } + + public function write($content) + { + $lines = explode("\n", $content); + for ($i=0,$c=count($lines); $i<$c; $i++) { + if ($this->indentationLevel > 0 + && !empty($lines[$i]) + && (empty($this->content) || "\n" === substr($this->content, -1))) { + $this->content .= str_repeat(' ', $this->indentationLevel * $this->indentationSpaces); + } + + $this->content .= $lines[$i]; + + if ($i+1 < $c) { + $this->content .= "\n"; + } + } + + return $this; + } + + public function rtrim() + { + $addNl = "\n" === substr($this->content, -1); + $this->content = rtrim($this->content); + + if ($addNl) { + $this->content .= "\n"; + } + + return $this; + } + + public function reset() + { + $this->content = ''; + $this->indentationLevel = 0; + + return $this; + } + + public function getContent() + { + return $this->content; + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Proxy/Enhancer.php b/vendor/jms/cg/src/CG/Proxy/Enhancer.php new file mode 100644 index 0000000..2ba73ed --- /dev/null +++ b/vendor/jms/cg/src/CG/Proxy/Enhancer.php @@ -0,0 +1,158 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Proxy; + +use CG\Core\NamingStrategyInterface; + +use CG\Generator\Writer; +use CG\Generator\PhpMethod; +use CG\Generator\PhpDocblock; +use CG\Generator\PhpClass; +use CG\Core\AbstractClassGenerator; + +/** + * Class enhancing generator implementation. + * + * This class enhances existing classes by generating a proxy and leveraging + * different generator implementation. + * + * There are several built-in generator such as lazy-initializing objects, or + * a generator for creating AOP joinpoints. + * + * @author Johannes M. Schmitt + */ +class Enhancer extends AbstractClassGenerator +{ + private $generatedClass; + private $class; + private $interfaces; + private $generators; + + public function __construct(\ReflectionClass $class, array $interfaces = array(), array $generators = array()) + { + if (empty($generators) && empty($interfaces)) { + throw new \RuntimeException('Either generators, or interfaces must be given.'); + } + + $this->class = $class; + $this->interfaces = $interfaces; + $this->generators = $generators; + } + + /** + * Creates a new instance of the enhanced class. + * + * @param array $args + * @return object + */ + public function createInstance(array $args = array()) + { + $generatedClass = $this->getClassName($this->class); + + if (!class_exists($generatedClass, false)) { + eval($this->generateClass()); + } + + $ref = new \ReflectionClass($generatedClass); + + return $ref->newInstanceArgs($args); + } + + public function writeClass($filename) + { + if (!is_dir($dir = dirname($filename))) { + if (false === @mkdir($dir, 0777, true)) { + throw new \RuntimeException(sprintf('Could not create directory "%s".', $dir)); + } + } + + if (!is_writable($dir)) { + throw new \RuntimeException(sprintf('The directory "%s" is not writable.', $dir)); + } + + file_put_contents($filename, "generateClass()); + } + + /** + * Creates a new enhanced class + * + * @return string + */ + public final function generateClass() + { + static $docBlock; + if (empty($docBlock)) { + $writer = new Writer(); + $writer + ->writeln('/**') + ->writeln(' * CG library enhanced proxy class.') + ->writeln(' *') + ->writeln(' * This code was generated automatically by the CG library, manual changes to it') + ->writeln(' * will be lost upon next generation.') + ->writeln(' */') + ; + $docBlock = $writer->getContent(); + } + + $this->generatedClass = PhpClass::create() + ->setDocblock($docBlock) + ->setParentClassName($this->class->name) + ; + + $proxyClassName = $this->getClassName($this->class); + if (false === strpos($proxyClassName, NamingStrategyInterface::SEPARATOR)) { + throw new \RuntimeException(sprintf('The proxy class name must be suffixed with "%s" and an optional string, but got "%s".', NamingStrategyInterface::SEPARATOR, $proxyClassName)); + } + $this->generatedClass->setName($proxyClassName); + + if (!empty($this->interfaces)) { + $this->generatedClass->setInterfaceNames(array_map(function($v) { return '\\'.$v; }, $this->interfaces)); + + foreach ($this->getInterfaceMethods() as $method) { + $method = PhpMethod::fromReflection($method); + $method->setAbstract(false); + + $this->generatedClass->setMethod($method); + } + } + + if (!empty($this->generators)) { + foreach ($this->generators as $generator) { + $generator->generate($this->class, $this->generatedClass); + } + } + + return $this->generateCode($this->generatedClass); + } + + /** + * Adds stub methods for the interfaces that have been implemented. + */ + protected function getInterfaceMethods() + { + $methods = array(); + + foreach ($this->interfaces as $interface) { + $ref = new \ReflectionClass($interface); + $methods = array_merge($methods, $ref->getMethods()); + } + + return $methods; + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Proxy/GeneratorInterface.php b/vendor/jms/cg/src/CG/Proxy/GeneratorInterface.php new file mode 100644 index 0000000..e641ec5 --- /dev/null +++ b/vendor/jms/cg/src/CG/Proxy/GeneratorInterface.php @@ -0,0 +1,38 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Proxy; + +use CG\Generator\PhpClass; + +/** + * Interface for enhancing generators. + * + * @author Johannes M. Schmitt + */ +interface GeneratorInterface +{ + /** + * Generates the necessary changes in the class. + * + * @param \ReflectionClass $originalClass + * @param PhpClass $generatedClass The generated class + * @return void + */ + function generate(\ReflectionClass $originalClass, PhpClass $generatedClass); +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Proxy/InterceptionGenerator.php b/vendor/jms/cg/src/CG/Proxy/InterceptionGenerator.php new file mode 100644 index 0000000..31a8ba6 --- /dev/null +++ b/vendor/jms/cg/src/CG/Proxy/InterceptionGenerator.php @@ -0,0 +1,117 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Proxy; + +use CG\Core\ClassUtils; + +use CG\Core\ReflectionUtils; + +use CG\Generator\PhpParameter; +use CG\Generator\PhpProperty; +use CG\Generator\PhpMethod; +use CG\Generator\PhpClass; + +/** + * Interception Generator. + * + * This generator creates joinpoints to allow for AOP advices. Right now, it only + * supports the most powerful around advice. + * + * @author Johannes M. Schmitt + */ +class InterceptionGenerator implements GeneratorInterface +{ + private $prefix = '__CGInterception__'; + private $filter; + private $requiredFile; + + public function setRequiredFile($file) + { + $this->requiredFile = $file; + } + + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } + + public function setFilter(\Closure $filter) + { + $this->filter = $filter; + } + + public function generate(\ReflectionClass $originalClass, PhpClass $genClass) + { + $methods = ReflectionUtils::getOverrideableMethods($originalClass); + + if (null !== $this->filter) { + $methods = array_filter($methods, $this->filter); + } + + if (empty($methods)) { + return; + } + + if (!empty($this->requiredFile)) { + $genClass->addRequiredFile($this->requiredFile); + } + + $interceptorLoader = new PhpProperty(); + $interceptorLoader + ->setName($this->prefix.'loader') + ->setVisibility(PhpProperty::VISIBILITY_PRIVATE) + ; + $genClass->setProperty($interceptorLoader); + + $loaderSetter = new PhpMethod(); + $loaderSetter + ->setName($this->prefix.'setLoader') + ->setVisibility(PhpMethod::VISIBILITY_PUBLIC) + ->setBody('$this->'.$this->prefix.'loader = $loader;') + ; + $genClass->setMethod($loaderSetter); + $loaderParam = new PhpParameter(); + $loaderParam + ->setName('loader') + ->setType('CG\Proxy\InterceptorLoaderInterface') + ; + $loaderSetter->addParameter($loaderParam); + + $interceptorCode = + '$ref = new \ReflectionMethod(%s, %s);'."\n" + .'$interceptors = $this->'.$this->prefix.'loader->loadInterceptors($ref, $this, array(%s));'."\n" + .'$invocation = new \CG\Proxy\MethodInvocation($ref, $this, array(%s), $interceptors);'."\n\n" + .'return $invocation->proceed();' + ; + + foreach ($methods as $method) { + $params = array(); + foreach ($method->getParameters() as $param) { + $params[] = '$'.$param->name; + } + $params = implode(', ', $params); + + $genMethod = PhpMethod::fromReflection($method) + ->setBody(sprintf($interceptorCode, var_export(ClassUtils::getUserClass($method->class), true), var_export($method->name, true), $params, $params)) + ->setDocblock(null) + ; + $genClass->setMethod($genMethod); + } + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Proxy/InterceptorLoaderInterface.php b/vendor/jms/cg/src/CG/Proxy/InterceptorLoaderInterface.php new file mode 100644 index 0000000..5deff19 --- /dev/null +++ b/vendor/jms/cg/src/CG/Proxy/InterceptorLoaderInterface.php @@ -0,0 +1,38 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Proxy; + +/** + * Interception Loader. + * + * Implementations of this interface are responsible for loading the interceptors + * for a certain method. + * + * @author Johannes M. Schmitt + */ +interface InterceptorLoaderInterface +{ + /** + * Loads interceptors. + * + * @param \ReflectionMethod $method + * @return array + */ + function loadInterceptors(\ReflectionMethod $method); +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Proxy/LazyInitializerGenerator.php b/vendor/jms/cg/src/CG/Proxy/LazyInitializerGenerator.php new file mode 100644 index 0000000..8591599 --- /dev/null +++ b/vendor/jms/cg/src/CG/Proxy/LazyInitializerGenerator.php @@ -0,0 +1,153 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Proxy; + +use CG\Generator\Writer; +use CG\Core\ReflectionUtils; +use CG\Generator\GeneratorUtils; +use CG\Generator\PhpParameter; +use CG\Generator\PhpMethod; +use CG\Generator\PhpProperty; +use CG\Generator\PhpClass; + +/** + * Generator for creating lazy-initializing instances. + * + * This generator enhances concrete classes to allow for them to be lazily + * initialized upon first access. + * + * @author Johannes M. Schmitt + */ +class LazyInitializerGenerator implements GeneratorInterface +{ + private $writer; + private $prefix = '__CG__'; + private $markerInterface; + + public function __construct() + { + $this->writer = new Writer(); + } + + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } + + /** + * Sets the marker interface which should be implemented by the + * generated classes. + * + * @param string $interface The FQCN of the interface + */ + public function setMarkerInterface($interface) + { + $this->markerInterface = $interface; + } + + /** + * Generates the necessary methods in the class. + * + * @param \ReflectionClass $originalClass + * @param PhpClass $class + * @return void + */ + public function generate(\ReflectionClass $originalClass, PhpClass $class) + { + $methods = ReflectionUtils::getOverrideableMethods($originalClass, true); + + // no public, non final methods + if (empty($methods)) { + return; + } + + if (null !== $this->markerInterface) { + $class->setImplementedInterfaces(array_merge( + $class->getImplementedInterfaces(), + array($this->markerInterface) + )); + } + + $initializer = new PhpProperty(); + $initializer->setName($this->prefix.'lazyInitializer'); + $initializer->setVisibility(PhpProperty::VISIBILITY_PRIVATE); + $class->setProperty($initializer); + + $initialized = new PhpProperty(); + $initialized->setName($this->prefix.'initialized'); + $initialized->setDefaultValue(false); + $initialized->setVisibility(PhpProperty::VISIBILITY_PRIVATE); + $class->setProperty($initialized); + + $initializerSetter = new PhpMethod(); + $initializerSetter->setName($this->prefix.'setLazyInitializer'); + $initializerSetter->setBody('$this->'.$this->prefix.'lazyInitializer = $initializer;'); + + $parameter = new PhpParameter(); + $parameter->setName('initializer'); + $parameter->setType('\CG\Proxy\LazyInitializerInterface'); + $initializerSetter->addParameter($parameter); + $class->setMethod($initializerSetter); + + $this->addMethods($class, $methods); + + $initializingMethod = new PhpMethod(); + $initializingMethod->setName($this->prefix.'initialize'); + $initializingMethod->setVisibility(PhpMethod::VISIBILITY_PRIVATE); + $initializingMethod->setBody( + $this->writer + ->reset() + ->writeln('if (null === $this->'.$this->prefix.'lazyInitializer) {') + ->indent() + ->writeln('throw new \RuntimeException("'.$this->prefix.'setLazyInitializer() must be called prior to any other public method on this object.");') + ->outdent() + ->write("}\n\n") + ->writeln('$this->'.$this->prefix.'lazyInitializer->initializeObject($this);') + ->writeln('$this->'.$this->prefix.'initialized = true;') + ->getContent() + ); + $class->setMethod($initializingMethod); + } + + private function addMethods(PhpClass $class, array $methods) + { + foreach ($methods as $method) { + $initializingCode = 'if (false === $this->'.$this->prefix.'initialized) {'."\n" + .' $this->'.$this->prefix.'initialize();'."\n" + .'}'; + + if ($class->hasMethod($method->name)) { + $genMethod = $class->getMethod($method->name); + $genMethod->setBody( + $initializingCode."\n" + .$genMethod->getBody() + ); + + continue; + } + + $genMethod = PhpMethod::fromReflection($method); + $genMethod->setBody( + $initializingCode."\n\n" + .'return '.GeneratorUtils::callMethod($method).';' + ); + $class->setMethod($genMethod); + } + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Proxy/LazyInitializerInterface.php b/vendor/jms/cg/src/CG/Proxy/LazyInitializerInterface.php new file mode 100644 index 0000000..7cfca5b --- /dev/null +++ b/vendor/jms/cg/src/CG/Proxy/LazyInitializerInterface.php @@ -0,0 +1,38 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Proxy; + +/** + * Lazy Initializer. + * + * Implementations of this interface are responsible for lazily initializing + * object instances. + * + * @author Johannes M. Schmitt + */ +interface LazyInitializerInterface +{ + /** + * Initializes the passed object. + * + * @param object $object + * @return void + */ + function initializeObject($object); +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Proxy/MethodInterceptorInterface.php b/vendor/jms/cg/src/CG/Proxy/MethodInterceptorInterface.php new file mode 100644 index 0000000..f1a7997 --- /dev/null +++ b/vendor/jms/cg/src/CG/Proxy/MethodInterceptorInterface.php @@ -0,0 +1,42 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Proxy; + +/** + * Interface for Method Interceptors. + * + * Implementations of this interface can execute custom code before, and after the + * invocation of the actual method. In addition, they can also catch, or throw + * exceptions, modify the return value, or modify the arguments. + * + * This is also known as around advice in AOP terminology. + * + * @author Johannes M. Schmitt + */ +interface MethodInterceptorInterface +{ + /** + * Called when intercepting a method call. + * + * @param MethodInvocation $invocation + * @return mixed the return value for the method invocation + * @throws \Exception may throw any exception + */ + function intercept(MethodInvocation $invocation); +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Proxy/MethodInvocation.php b/vendor/jms/cg/src/CG/Proxy/MethodInvocation.php new file mode 100644 index 0000000..11393f4 --- /dev/null +++ b/vendor/jms/cg/src/CG/Proxy/MethodInvocation.php @@ -0,0 +1,77 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Proxy; + +/** + * Represents a method invocation. + * + * This object contains information for the method invocation, such as the object + * on which the method is invoked, and the arguments that are passed to the method. + * + * Before the actual method is called, first all the interceptors must call the + * proceed() method on this class. + * + * @author Johannes M. Schmitt + */ +class MethodInvocation +{ + public $reflection; + public $object; + public $arguments; + + private $interceptors; + private $pointer; + + public function __construct(\ReflectionMethod $reflection, $object, array $arguments, array $interceptors) + { + $this->reflection = $reflection; + $this->object = $object; + $this->arguments = $arguments; + $this->interceptors = $interceptors; + $this->pointer = 0; + } + + /** + * Proceeds down the call-chain and eventually calls the original method. + * + * @return mixed + */ + public function proceed() + { + if (isset($this->interceptors[$this->pointer])) { + return $this->interceptors[$this->pointer++]->intercept($this); + } + + $this->reflection->setAccessible(true); + + return $this->reflection->invokeArgs($this->object, $this->arguments); + } + + /** + * Returns a string representation of the method. + * + * This is intended for debugging purposes only. + * + * @return string + */ + public function __toString() + { + return sprintf('%s::%s', $this->reflection->class, $this->reflection->name); + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Proxy/RegexInterceptionLoader.php b/vendor/jms/cg/src/CG/Proxy/RegexInterceptionLoader.php new file mode 100644 index 0000000..74a7c37 --- /dev/null +++ b/vendor/jms/cg/src/CG/Proxy/RegexInterceptionLoader.php @@ -0,0 +1,48 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace CG\Proxy; + +class RegexInterceptionLoader implements InterceptorLoaderInterface +{ + private $interceptors; + + public function __construct(array $interceptors = array()) + { + $this->interceptors = $interceptors; + } + + public function loadInterceptors(\ReflectionMethod $method) + { + $signature = $method->class.'::'.$method->name; + + $matchingInterceptors = array(); + foreach ($this->interceptors as $pattern => $interceptor) { + if (preg_match('#'.$pattern.'#', $signature)) { + $matchingInterceptors[] = $this->initializeInterceptor($interceptor); + } + } + + return $matchingInterceptors; + } + + protected function initializeInterceptor($interceptor) + { + return $interceptor; + } +} \ No newline at end of file diff --git a/vendor/jms/cg/src/CG/Version.php b/vendor/jms/cg/src/CG/Version.php new file mode 100644 index 0000000..0ca6842 --- /dev/null +++ b/vendor/jms/cg/src/CG/Version.php @@ -0,0 +1,8 @@ + + */ +final class AfterSetup +{ +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/DoctrineListener.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/DoctrineListener.php new file mode 100644 index 0000000..3a834f0 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/DoctrineListener.php @@ -0,0 +1,74 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Annotation; + +use JMS\DiExtraBundle\Exception\InvalidTypeException; + +/** + * @Annotation + * @Target("CLASS") + */ +class DoctrineListener +{ + /** @var array @Required */ + public $events; + + /** @var string */ + public $connection; + + /** @var boolean */ + public $lazy = true; + + /** @var integer */ + public $priority = 0; + + public function __construct() + { + if (0 === func_num_args()) { + return; + } + $values = func_get_arg(0); + + if (!isset($values['value'])) { + throw new InvalidTypeException('DoctrineListener', 'value', 'array or string', null); + } + $this->events = (array) $values['value']; + + if (isset($values['connection'])) { + if (!is_string($values['connection'])) { + throw new InvalidTypeException('DoctrineListener', 'connection', 'string', $values['connection']); + } + $this->connection = $values['connection']; + } + + if (isset($values['lazy'])) { + if (!is_boolean($values['lazy'])) { + throw new InvalidTypeException('DoctrineListener', 'lazy', 'boolean', $values['lazy']); + } + $this->lazy = $values['lazy']; + } + + if (isset($values['priority'])) { + if (!is_integer($values['priority'])) { + throw new InvalidTypeException('DoctrineListener', 'priority', 'integer', $values['priority']); + } + $this->priority = $values['priority']; + } + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/FormType.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/FormType.php new file mode 100644 index 0000000..073b971 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/FormType.php @@ -0,0 +1,37 @@ + + */ +final class FormType +{ + /** @var string */ + public $alias; + + public function __construct() + { + if (0 === func_num_args()) { + return; + } + $values = func_get_arg(0); + + if (isset($values['value'])) { + $values['alias'] = $values['value']; + } + + if (isset($values['alias'])) { + if (!is_string($values['alias'])) { + throw new InvalidTypeException('FormType', 'alias', 'string', $values['alias']); + } + + $this->alias = $values['alias']; + } + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Inject.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Inject.php new file mode 100644 index 0000000..f723003 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Inject.php @@ -0,0 +1,27 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Annotation; + +/** + * @Annotation + * @Target({"PROPERTY", "ANNOTATION"}) + */ +final class Inject extends Reference +{ +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/InjectParams.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/InjectParams.php new file mode 100644 index 0000000..44a4d82 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/InjectParams.php @@ -0,0 +1,57 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Annotation; + +use JMS\DiExtraBundle\Exception\InvalidTypeException; + +/** + * @Annotation + * @Target("METHOD") + */ +final class InjectParams +{ + /** @var array */ + public $params = array(); + + public function __construct() + { + if (0 === func_num_args()) { + return; + } + $values = func_get_arg(0); + + if (isset($values['params'])) { + $values['value'] = $values['params']; + } + + if (isset($values['value'])) { + if (!is_array($values['value'])) { + throw new InvalidTypeException('InjectParams', 'value', 'array', $values['value']); + } + + foreach ($values['value'] as $k => $v) { + if (!$v instanceof Inject) { + throw new InvalidTypeException('InjectParams', sprintf('value[%s]', $k), '@Inject', $v); + } + + $this->params[$k] = $v; + } + } + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/LookupMethod.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/LookupMethod.php new file mode 100644 index 0000000..30fef73 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/LookupMethod.php @@ -0,0 +1,27 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Annotation; + +/** + * @Annotation + * @Target("METHOD") + */ +final class LookupMethod extends Reference +{ +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Observe.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Observe.php new file mode 100644 index 0000000..000f827 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Observe.php @@ -0,0 +1,62 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Annotation; + +use JMS\DiExtraBundle\Exception\InvalidTypeException; + +/** + * @Annotation + * @Target("METHOD") + */ +final class Observe +{ + /** @var string @Required */ + public $event; + + /** @var integer */ + public $priority = 0; + + public function __construct() + { + if (0 === func_num_args()) { + return; + } + $values = func_get_arg(0); + + if (isset($values['event'])) { + $values['value'] = $values['event']; + } + + if (isset($values['value'])) { + if (!is_string($values['value'])) { + throw new InvalidTypeException('Observe', 'value', 'string', $values['value']); + } + + $this->event = $values['value']; + } + + if (isset($values['priority'])) { + if (!is_numeric($values['priority'])) { + throw new InvalidTypeException('Observe', 'priority', 'integer', $values['priority']); + } + + $this->priority = $values['priority']; + } + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Reference.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Reference.php new file mode 100644 index 0000000..853f514 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Reference.php @@ -0,0 +1,54 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Annotation; + +use JMS\DiExtraBundle\Exception\InvalidTypeException; + +abstract class Reference +{ + /** @var string */ + public $value; + + /** @var boolean */ + public $required; + + public final function __construct() + { + if (0 === func_num_args()) { + return; + } + $values = func_get_arg(0); + + if (isset($values['value'])) { + if (!is_string($values['value'])) { + throw new InvalidTypeException('Inject', 'value', 'string', $values['value']); + } + + $this->value = $values['value']; + } + + if (isset($values['required'])) { + if (!is_bool($values['required'])) { + throw new InvalidTypeException('Inject', 'required', 'boolean', $values['required']); + } + + $this->required = $values['required']; + } + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Service.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Service.php new file mode 100644 index 0000000..caa0557 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Service.php @@ -0,0 +1,87 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Annotation; + +use JMS\DiExtraBundle\Exception\InvalidTypeException; + +/** + * @Annotation + * @Target("CLASS") + */ +final class Service +{ + /** @var string */ + public $id; + + /** @var string */ + public $parent; + + /** @var boolean */ + public $public; + + /** @var string */ + public $scope; + + /** @var boolean */ + public $abstract; + + public function __construct() + { + if (0 === func_num_args()) { + return; + } + $values = func_get_arg(0); + + if (isset($values['value'])) { + if (!is_string($values['value'])) { + throw new InvalidTypeException('Service', 'value', 'string', $values['value']); + } + + $this->id = $values['value']; + } + if (isset($values['parent'])) { + if (!is_string($values['parent'])) { + throw new InvalidTypeException('Service', 'parent', 'string', $values['parent']); + } + + $this->parent = $values['parent']; + } + if (isset($values['public'])) { + if (!is_bool($values['public'])) { + throw new InvalidTypeException('Service', 'public', 'boolean', $values['public']); + } + + $this->public = $values['public']; + } + if (isset($values['scope'])) { + if (!is_string($values['scope'])) { + throw new InvalidTypeException('Service', 'scope', 'string', $values['scope']); + } + + $this->scope = $values['scope']; + } + if (isset($values['abstract'])) { + if (!is_bool($values['abstract'])) { + throw new InvalidTypeException('Service', 'abstract', 'boolean', $values['abstract']); + } + + $this->abstract = $values['abstract']; + } + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Tag.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Tag.php new file mode 100644 index 0000000..55942be --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Tag.php @@ -0,0 +1,60 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Annotation; + +use JMS\DiExtraBundle\Exception\InvalidArgumentException; +use JMS\DiExtraBundle\Exception\InvalidTypeException; + +/** + * @Annotation + * @Target("CLASS") + */ +final class Tag +{ + /** @var string @Required */ + public $name; + + /** @var array */ + public $attributes = array(); + + public function __construct() + { + if (0 === func_num_args()) { + return; + } + $values = func_get_arg(0); + + if (!isset($values['value'])) { + throw new InvalidArgumentException('A value must be given for annotation "@Tag".'); + } + if (!is_string($values['value'])) { + throw new InvalidTypeException('Tag', 'value', 'string', $values['value']); + } + + $this->name = $values['value']; + + if (isset($values['attributes'])) { + if (!is_array($values['attributes'])) { + throw new InvalidTypeException('Tag', 'attributes', 'array', $values['attributes']); + } + + $this->attributes = $values['attributes']; + } + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Validator.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Validator.php new file mode 100644 index 0000000..cc8e38a --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Annotation/Validator.php @@ -0,0 +1,51 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Annotation; + +use JMS\DiExtraBundle\Exception\InvalidTypeException; + +/** + * @Annotation + * @Target("CLASS") + */ +final class Validator +{ + /** @var string @Required */ + public $alias; + + public function __construct() + { + if (0 === func_num_args()) { + return; + } + $values = func_get_arg(0); + + if (isset($values['alias'])) { + $values['value'] = $values['alias']; + } + + if (!isset($values['value'])) { + throw new \InvalidArgumentException('A value must be given for @Validator annotations.'); + } + if (!is_string($values['value'])) { + throw new InvalidTypeException('Validator', 'value', 'string', $values['value']); + } + $this->alias = $values['value']; + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Config/FastDirectoriesResource.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Config/FastDirectoriesResource.php new file mode 100644 index 0000000..719ac41 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Config/FastDirectoriesResource.php @@ -0,0 +1,78 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Config; + +use JMS\DiExtraBundle\Finder\PatternFinder; + +use Symfony\Component\Config\Resource\ResourceInterface; + +class FastDirectoriesResource implements ResourceInterface +{ + private $finder; + + private $directories; + private $filePattern; + private $files = array(); + + public function __construct(array $directories, $filePattern = null) + { + $this->finder = new PatternFinder('.*', '*.php'); + $this->finder->setRegexPattern(true); + + $this->directories = $directories; + $this->filePattern = $filePattern ?: '*'; + } + + public function __toString() + { + return implode(', ', $this->directories); + } + + public function getResource() + { + return $this->directories; + } + + public function update() + { + $this->files = $this->getFiles(); + } + + public function isFresh($timestamp) + { + $files = $this->getFiles(); + + if (array_diff($this->files, $files) || array_diff($files, $this->files)) { + return false; + } + + foreach ($files as $file) { + if (filemtime($file) > $timestamp) { + return false; + } + } + + return true; + } + + private function getFiles() + { + return $this->finder->findFiles($this->directories); + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Config/ServiceFilesResource.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Config/ServiceFilesResource.php new file mode 100644 index 0000000..40508b2 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Config/ServiceFilesResource.php @@ -0,0 +1,52 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Config; + +use JMS\DiExtraBundle\Finder\PatternFinder; +use Symfony\Component\Config\Resource\ResourceInterface; + +class ServiceFilesResource implements ResourceInterface +{ + private $files; + private $dirs; + + public function __construct(array $files, array $dirs) + { + $this->files = $files; + $this->dirs = $dirs; + } + + public function isFresh($timestamp) + { + $finder = new PatternFinder('JMS\DiExtraBundle\Annotation'); + $files = $finder->findFiles($this->dirs); + + return !array_diff($files, $this->files) && !array_diff($this->files, $files); + } + + public function __toString() + { + return implode(', ', $this->files); + } + + public function getResource() + { + return array($this->files, $this->dirs); + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/Compiler/AnnotationConfigurationPass.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/Compiler/AnnotationConfigurationPass.php new file mode 100644 index 0000000..cb8d77b --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/Compiler/AnnotationConfigurationPass.php @@ -0,0 +1,120 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Alias; +use JMS\DiExtraBundle\Exception\RuntimeException; +use JMS\DiExtraBundle\Config\ServiceFilesResource; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Definition; +use JMS\DiExtraBundle\Finder\PatternFinder; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +class AnnotationConfigurationPass implements CompilerPassInterface +{ + private $kernel; + private $finder; + + public function __construct(KernelInterface $kernel) + { + $this->kernel = $kernel; + $this->finder = new PatternFinder('JMS\DiExtraBundle\Annotation'); + } + + public function process(ContainerBuilder $container) + { + $reader = $container->get('annotation_reader'); + $factory = $container->get('jms_di_extra.metadata.metadata_factory'); + $converter = $container->get('jms_di_extra.metadata.converter'); + + $directories = $this->getScanDirectories($container); + if (!$directories) { + $container->getCompiler()->addLogMessage('No directories configured for AnnotationConfigurationPass.'); + return; + } + + $files = $this->finder->findFiles($directories); + $container->addResource(new ServiceFilesResource($files, $directories)); + foreach ($files as $file) { + $container->addResource(new FileResource($file)); + require_once $file; + + $className = $this->getClassName($file); + + if (null === $metadata = $factory->getMetadataForClass($className)) { + continue; + } + if (null === $metadata->getOutsideClassMetadata()->id) { + continue; + } + + foreach ($converter->convert($metadata) as $id => $definition) { + $container->setDefinition($id, $definition); + } + } + } + + private function getScanDirectories(ContainerBuilder $c) + { + $bundles = $this->kernel->getBundles(); + $scanBundles = $c->getParameter('jms_di_extra.bundles'); + $scanAllBundles = $c->getParameter('jms_di_extra.all_bundles'); + + $directories = $c->getParameter('jms_di_extra.directories'); + foreach ($bundles as $name => $bundle) { + if (!$scanAllBundles && !in_array($name, $scanBundles, true)) { + continue; + } + + if ('JMSDiExtraBundle' === $name) { + continue; + } + + $directories[] = $bundle->getPath(); + } + + return $directories; + } + + /** + * Only supports one namespaced class per file + * + * @throws \RuntimeException if the class name cannot be extracted + * @param string $filename + * @return string the fully qualified class name + */ + private function getClassName($filename) + { + $src = file_get_contents($filename); + + if (!preg_match('/\bnamespace\s+([^;]+);/s', $src, $match)) { + throw new RuntimeException(sprintf('Namespace could not be determined for file "%s".', $filename)); + } + $namespace = $match[1]; + + if (!preg_match('/\bclass\s+([^\s]+)\s+(?:extends|implements|{)/s', $src, $match)) { + throw new RuntimeException(sprintf('Could not extract class name from file "%s".', $filename)); + } + + return $namespace.'\\'.$match[1]; + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/Compiler/IntegrationPass.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/Compiler/IntegrationPass.php new file mode 100644 index 0000000..db3e488 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/Compiler/IntegrationPass.php @@ -0,0 +1,55 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Integrates the bundle with external code. + * + * @author Johannes M. Schmitt + */ +class IntegrationPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + // replace Symfony2's default controller resolver + $container->setAlias('controller_resolver', new Alias('jms_di_extra.controller_resolver', false)); + + // replace SensioFrameworkExtraBundle's default template listener + if ($container->hasDefinition('sensio_framework_extra.view.listener')) { + $def = $container->getDefinition('sensio_framework_extra.view.listener'); + + // only overwrite if it has the default class otherwise the user has to do the integration manually + if ('%sensio_framework_extra.view.listener.class%' === $def->getClass()) { + $def->setClass('%jms_di_extra.template_listener.class%'); + } + } + + if ($container->hasDefinition('sensio_framework_extra.controller.listener')) { + $def = $container->getDefinition('sensio_framework_extra.controller.listener'); + + if ('%sensio_framework_extra.controller.listener.class%' === $def->getClass()) { + $def->setClass('%jms_di_extra.controller_listener.class%'); + } + } + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/Compiler/ResourceOptimizationPass.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/Compiler/ResourceOptimizationPass.php new file mode 100644 index 0000000..ee73a0e --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/Compiler/ResourceOptimizationPass.php @@ -0,0 +1,81 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\DependencyInjection\Compiler; + +use JMS\DiExtraBundle\Config\FastDirectoriesResource; + +use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +class ResourceOptimizationPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $resources = $directories = array(); + + $ref = new \ReflectionProperty('Symfony\Component\Config\Resource\DirectoryResource', 'pattern'); + $ref->setAccessible(true); + + foreach ($container->getResources() as $resource) { + if ($resource instanceof DirectoryResource) { + if (null === $pattern = $ref->getValue($resource)) { + $pattern = '*'; + } + + $directories[$pattern][] = $resource->getResource(); + + continue; + } + + $resources[] = $resource; + } + + $sortFunc = function($a, $b) { + return strlen($a) - strlen($b); + }; + + foreach ($directories as $pattern => $pDirectories) { + $newResources = array(); + + usort($pDirectories, $sortFunc); + foreach ($pDirectories as $a) { + foreach ($newResources as $b) { + if (0 === strpos($a, $b)) { + continue 2; + } + } + + $newResources[] = $a; + } + + $directories[$pattern] = $newResources; + } + + foreach ($directories as $pattern => $pDirectories) { + $newResource = new FastDirectoriesResource($pDirectories, $pattern); + $newResource->update(); + $resources[] = $newResource; + } + + $ref = new \ReflectionProperty('Symfony\Component\DependencyInjection\ContainerBuilder', 'resources'); + $ref->setAccessible(true); + $ref->setValue($container, $resources); + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/Configuration.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..52129b8 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/Configuration.php @@ -0,0 +1,69 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +class Configuration implements ConfigurationInterface +{ + public function getConfigTreeBuilder() + { + $tb = new TreeBuilder(); + + $tb + ->root('jms_di_extra', 'array') + ->children() + ->arrayNode('locations') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('all_bundles')->defaultFalse()->end() + ->arrayNode('bundles') + ->beforeNormalization() + ->ifString() + ->then(function($v) { + return preg_split('/\s*,\s*/', $v); + }) + ->end() + ->prototype('scalar')->end() + ->end() + ->arrayNode('directories') + ->beforeNormalization() + ->ifString() + ->then(function($v) { + return preg_split('/\s*,\s*/', $v); + }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->scalarNode('cache_dir')->defaultValue('%kernel.cache_dir%/diextra')->end() + ->arrayNode('metadata') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('cache')->defaultValue('file')->cannotBeEmpty()->end() + ->end() + ->end() + ->end() + ->end(); + + return $tb; + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/JMSDiExtraExtension.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/JMSDiExtraExtension.php new file mode 100644 index 0000000..9c0266c --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/JMSDiExtraExtension.php @@ -0,0 +1,84 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\DependencyInjection; + +use JMS\DiExtraBundle\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; + +class JMSDiExtraExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $config = $this->mergeConfigs($configs); + + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.xml'); + + $container->setParameter('jms_di_extra.all_bundles', $config['locations']['all_bundles']); + $container->setParameter('jms_di_extra.bundles', $config['locations']['bundles']); + $container->setParameter('jms_di_extra.directories', $config['locations']['directories']); + $container->setParameter('jms_di_extra.cache_dir', $config['cache_dir']); + + $this->configureMetadata($config['metadata'], $container, $config['cache_dir'].'/metadata'); + + $this->addClassesToCompile(array( + 'JMS\\DiExtraBundle\\HttpKernel\ControllerResolver', + )); + } + + private function configureMetadata(array $config, $container, $cacheDir) + { + if ('none' === $config['cache']) { + $container->removeAlias('jms_di_extra.metadata.cache'); + return; + } + + if ('file' === $config['cache']) { + $cacheDir = $container->getParameterBag()->resolveValue($cacheDir); + if (!file_exists($cacheDir)) { + if (false === @mkdir($cacheDir, 0777, true)) { + throw new RuntimeException(sprintf('The cache dir "%s" could not be created.', $cacheDir)); + } + } + if (!is_writable($cacheDir)) { + throw new RuntimeException(sprintf('The cache dir "%s" is not writable.', $cacheDir)); + } + + $container + ->getDefinition('jms_di_extra.metadata.cache.file_cache') + ->replaceArgument(0, $cacheDir) + ; + } else { + $container->setAlias('jms_di_extra.metadata.cache', new Alias($config['cache'], false)); + } + } + + private function mergeConfigs(array $configs) + { + $processor = new Processor(); + $configuration = new Configuration(); + + return $processor->process($configuration->getConfigTreeBuilder()->buildTree(), $configs); + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/LookupMethodClassInterface.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/LookupMethodClassInterface.php new file mode 100644 index 0000000..40a506f --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/DependencyInjection/LookupMethodClassInterface.php @@ -0,0 +1,24 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\DependencyInjection; + +interface LookupMethodClassInterface +{ + function __jmsDiExtra_getOriginalClassName(); +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/EventListener/ControllerListener.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/EventListener/ControllerListener.php new file mode 100644 index 0000000..cab730f --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/EventListener/ControllerListener.php @@ -0,0 +1,58 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\EventListener; + +use Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener as BaseControllerListener; +use CG\Core\ClassUtils; +use Doctrine\Common\Annotations\Reader; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface; + +/** + * The ControllerListener class parses annotation blocks located in + * controller classes. + * + * @author Fabien Potencier + */ +class ControllerListener extends BaseControllerListener +{ + /** + * Modifies the Request object to apply configuration information found in + * controllers annotations like the template to render or HTTP caching + * configuration. + * + * @param FilterControllerEvent $event A FilterControllerEvent instance + */ + public function onKernelController(FilterControllerEvent $event) + { + if (!is_array($controller = $event->getController())) { + return; + } + + $object = new \ReflectionClass(ClassUtils::getUserClass(get_class($controller[0]))); + $method = $object->getMethod($controller[1]); + + $request = $event->getRequest(); + foreach ($this->reader->getMethodAnnotations($method) as $configuration) { + if ($configuration instanceof ConfigurationInterface) { + $request->attributes->set('_'.$configuration->getAliasName(), $configuration); + } + } + } +} diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/EventListener/TemplateListener.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/EventListener/TemplateListener.php new file mode 100644 index 0000000..feacc35 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/EventListener/TemplateListener.php @@ -0,0 +1,50 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\EventListener; + +use CG\Core\ClassUtils; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Component\HttpFoundation\Request; +use JMS\DiExtraBundle\DependencyInjection\LookupMethodClassInterface; +use Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener as FrameworkExtraTemplateListener; + +class TemplateListener extends FrameworkExtraTemplateListener +{ + protected function guessTemplateName($controller, Request $request, $engine = 'twig') + { + $controllerClass = get_class($controller[0]); + $userClass = ClassUtils::getUserClass($controllerClass); + + if ($controllerClass === $userClass) { + return parent::guessTemplateName($controller, $request, $engine); + } + + if (!preg_match('/Controller\\\(.+)Controller$/', $userClass, $matchController)) { + throw new \InvalidArgumentException(sprintf('The "%s" class does not look like a controller class (it must be in a "Controller" sub-namespace and the class name must end with "Controller")', $userClass)); + } + + if (!preg_match('/^(.+)Action$/', $controller[1], $matchAction)) { + throw new \InvalidArgumentException(sprintf('The "%s" method does not look like an action method (it does not end with Action)', $controller[1])); + } + + $bundle = $this->getBundleForClass($userClass); + + return new TemplateReference($bundle->getName(), $matchController[1], $matchAction[1], $request->getRequestFormat(), $engine); + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Exception/Exception.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Exception/Exception.php new file mode 100644 index 0000000..293e70b --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Exception/Exception.php @@ -0,0 +1,28 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Exception; + +/** + * Base exception for the DiExtraBundle. + * + * @author Johannes M. Schmitt + */ +interface Exception +{ +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Exception/InvalidArgumentException.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..990c60f --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Exception/InvalidArgumentException.php @@ -0,0 +1,28 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Exception; + +/** + * InvalidArgumentException for the DiExtraBundle. + * + * @author Johannes M. Schmitt + */ +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Exception/InvalidTypeException.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Exception/InvalidTypeException.php new file mode 100644 index 0000000..7437778 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Exception/InvalidTypeException.php @@ -0,0 +1,29 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Exception; + +class InvalidTypeException extends InvalidArgumentException +{ + public function __construct($annotName, $attrName, $expected, $actual) + { + $msg = sprintf('The attribute "%s" on annotation "@%s" is expected to be of type %s, but got %s.', $attrName, $annotName, $expected, gettype($actual)); + + parent::__construct($msg); + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Exception/RuntimeException.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Exception/RuntimeException.php new file mode 100644 index 0000000..a47a6e4 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Exception/RuntimeException.php @@ -0,0 +1,28 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Exception; + +/** + * RuntimeException for the DiExtraBundle. + * + * @author Johannes M. Schmitt + */ +class RuntimeException extends \RuntimeException implements Exception +{ +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Finder/PatternFinder.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Finder/PatternFinder.php new file mode 100644 index 0000000..f4bea5f --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Finder/PatternFinder.php @@ -0,0 +1,201 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Finder; + +use JMS\DiExtraBundle\Exception\RuntimeException; +use Symfony\Component\Finder\Finder; +use Symfony\Component\Process\ExecutableFinder; + +class PatternFinder +{ + const METHOD_GREP = 1; + const METHOD_FINDSTR = 2; + const METHOD_FINDER = 3; + + private static $method; + private static $grepPath; + + private $pattern; + private $filePattern; + private $recursive = true; + private $regexPattern = false; + + public function __construct($pattern, $filePattern = '*.php') + { + if (null === self::$method) { + self::determineMethod(); + } + + $this->pattern = $pattern; + $this->filePattern = $filePattern; + } + + public function setRecursive($bool) + { + $this->recursive = (Boolean) $bool; + } + + public function setRegexPattern($bool) + { + $this->regexPattern = (Boolean) $bool; + } + + public function findFiles(array $dirs) + { + // check for grep availability + if (self::METHOD_GREP === self::$method) { + return $this->findUsingGrep($dirs); + } + + // use FINDSTR on Windows + if (self::METHOD_FINDSTR === self::$method) { + return $this->findUsingFindstr($dirs); + } + + // this should really be avoided at all costs since it is damn slow + return $this->findUsingFinder($dirs); + } + + private function findUsingFindstr(array $dirs) + { + $cmd = 'FINDSTR /M /S /P'; + + if (!$this->recursive) { + $cmd .= ' /L'; + } + + $cmd .= ' /D:'.escapeshellarg(implode(';', $dirs)); + $cmd .= ' '.escapeshellarg($this->pattern); + $cmd .= ' '.$this->filePattern; + + exec($cmd, $lines, $exitCode); + + if (1 === $exitCode) { + return array(); + } + + if (0 !== $exitCode) { + throw new RuntimeException(sprintf('Command "%s" exited with non-successful status code. "%d".', $cmd, $exitCode)); + } + + // Looks like FINDSTR has different versions with different output formats. + // + // Supported format #1: + // C:\matched\dir1: + // Relative\Path\To\File1.php + // Relative\Path\To\File2.php + // C:\matched\dir2: + // Relative\Path\To\File3.php + // Relative\Path\To\File4.php + // + // Supported format #2: + // C:\matched\dir1\Relative\Path\To\File1.php + // C:\matched\dir1\Relative\Path\To\File2.php + // C:\matched\dir2\Relative\Path\To\File3.php + // C:\matched\dir2\Relative\Path\To\File4.php + + $files = array(); + $currentDir = ''; + foreach ($lines as $line) { + if (':' === substr($line, -1)) { + $currentDir = trim($line, ' :/').'/'; + continue; + } + + $files[] = $currentDir.$line; + } + + return $files; + } + + private function findUsingGrep(array $dirs) + { + $cmd = self::$grepPath; + + if (!$this->regexPattern) { + $cmd .= ' --fixed-strings'; + } else { + $cmd .= ' --extended-regexp'; + } + + if ($this->recursive) { + $cmd .= ' --directories=recurse'; + } else { + $cmd .= ' --directories=skip'; + } + + $cmd .= ' --devices=skip --files-with-matches --with-filename --max-count=1 --color=never --include='.$this->filePattern; + $cmd .= ' '.escapeshellarg($this->pattern); + + foreach ($dirs as $dir) { + $cmd .= ' '.escapeshellarg($dir); + } + exec($cmd, $files, $exitCode); + + if (1 === $exitCode) { + return array(); + } + + if (0 !== $exitCode) { + throw new RuntimeException(sprintf('Command "%s" exited with non-successful status code "%d".', $cmd, $exitCode)); + } + + return $files; + } + + private function findUsingFinder(array $dirs) + { + $finder = new Finder(); + $pattern = $this->pattern; + $regex = $this->regexPattern; + $finder + ->files() + ->name($this->filePattern) + ->in($dirs) + ->ignoreVCS(true) + ->filter(function($file) use ($pattern, $regex) { + if (!$regex) { + return false !== strpos(file_get_contents($file->getPathName()), $pattern); + } + + return 0 < preg_match('#'.$pattern.'#', file_get_contents($file->getPathName())); + }) + ; + + if (!$this->recursive) { + $finder->depth('<= 0'); + } + + return array_keys(iterator_to_array($finder)); + } + + private static function determineMethod() + { + $finder = new ExecutableFinder(); + $isWindows = 0 === stripos(PHP_OS, 'win'); + + if (!$isWindows && self::$grepPath = $finder->find('grep')) { + self::$method = self::METHOD_GREP; + } else if ($isWindows) { + self::$method = self::METHOD_FINDSTR; + } else { + self::$method = self::METHOD_FINDER; + } + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Generator/DefinitionInjectorGenerator.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Generator/DefinitionInjectorGenerator.php new file mode 100644 index 0000000..3bfea60 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Generator/DefinitionInjectorGenerator.php @@ -0,0 +1,192 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Generator; + +use CG\Generator\Writer; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Definition; + +/** + * Generates lightweight code for injecting a single definition. + * + * @author Johannes M. Schmitt + */ +class DefinitionInjectorGenerator +{ + private $nameGenerator; + private $inlinedDefinitions; + + public function __construct() + { + $this->nameGenerator = new NameGenerator(); + $this->inlinedDefinitions = new \SplObjectStorage(); + } + + public function generate(Definition $def) + { + $writer = new Writer(); + + $writer + ->writeln('writeln('/**') + ->writeln(' * This code has been auto-generated by the JMSDiExtraBundle.') + ->writeln(' *') + ->writeln(' * Manual changes to it will be lost.') + ->writeln(' */') + ->writeln('return function($container) {') + ->indent() + ; + + if ($file = $def->getFile()) { + $writer->writeln('require_once '.var_export($file, true).';'); + + require_once $file; + } + + foreach ($this->getInlineDefinitions($def) as $inlineDef) { + $name = $this->nameGenerator->nextName(); + $this->inlinedDefinitions[$inlineDef] = $name; + + $writer->writeln('$'.$name.' = new \\'.$inlineDef->getClass().$this->dumpArguments($inlineDef->getArguments()).';'); + } + + $writer->writeln('$instance = new \\'.$def->getClass().$this->dumpArguments($def->getArguments()).';'); + + foreach ($def->getMethodCalls() as $call) { + list($method, $arguments) = $call; + $writer->writeln('$instance->'.$method.$this->dumpArguments($arguments).';'); + } + + $ref = new \ReflectionClass($def->getClass()); + foreach ($def->getProperties() as $property => $value) { + $refProperty = $this->getReflectionProperty($ref, $property); + + if ($refProperty->isPublic()) { + $writer->writeln('$instance->'.$property.' = '.$this->dumpValue($value).';'); + } else { + $writer + ->writeln(sprintf("\$refProperty = new \ReflectionProperty(%s, %s);", var_export($refProperty->getDeclaringClass()->getName(), true), var_export($property, true))) + ->writeln('$refProperty->setAccessible(true);') + ->writeln('$refProperty->setValue($instance, '.$this->dumpValue($value).');') + ; + } + } + + if (method_exists($def, 'getInitMethod') && $def->getInitMethod()) { + $writer->writeln('$instance->'.$def->getInitMethod().'();'); + } + + $writer + ->writeln('return $instance;') + ->outdent() + ->writeln('};') + ; + + return $writer->getContent(); + } + + private function getReflectionProperty($ref, $property) + { + $origClass = $ref->getName(); + while (!$ref->hasProperty($property) && false !== $ref = $ref->getParentClass()); + + if (!$ref->hasProperty($property)) { + throw new \RuntimeException(sprintf('Could not find property "%s" anywhere in class "%s" or one of its parents.', $property, $origName)); + } + + return $ref->getProperty($property); + } + + private function getInlineDefinitions(Definition $def) + { + $defs = new \SplObjectStorage(); + $this->getDefinitionsFromArray($def->getArguments(), $defs); + $this->getDefinitionsFromArray($def->getMethodCalls(), $defs); + $this->getDefinitionsFromArray($def->getProperties(), $defs); + + return $defs; + } + + private function getDefinitionsFromArray(array $a, \SplObjectStorage $defs) + { + foreach ($a as $k => $v) { + if ($v instanceof Definition) { + $defs->attach($v); + } else if (is_array($v)) { + $this->getDefinitionsFromArray($v, $defs); + } + } + } + + private function dumpArguments(array $arguments) + { + $code = '('; + + $first = true; + foreach ($arguments as $argument) { + if (!$first) { + $code .= ', '; + } + $first = false; + + $code .= $this->dumpValue($argument); + } + + return $code.')'; + } + + private function dumpValue($value) + { + if (is_array($value)) { + $code = 'array('; + + $first = true; + foreach ($value as $k => $v) { + if (!$first) { + $code .= ', '; + } + $first = false; + + $code .= sprintf('%s => %s', var_export($k, true), $this->dumpValue($v)); + } + + return $code.')'; + } else if ($value instanceof Reference) { + if ('service_container' === (string) $value) { + return '$container'; + } + + return sprintf('$container->get(%s, %d)', var_export((string) $value, true), $value->getInvalidBehavior()); + } else if ($value instanceof Parameter) { + return sprintf('$container->getParameter(%s)', var_export((string) $value, true)); + } else if (is_scalar($value) || null === $value) { + // we do not support embedded parameters + if (is_string($value) && '%' === $value[0] && '%' !== $value[1]) { + return sprintf('$container->getParameter(%s)', var_export(substr($value, 1, -1), true)); + } + + return var_export($value, true); + } else if ($value instanceof Definition) { + return sprintf('$%s', $this->inlinedDefinitions[$value]); + } + + throw new \RuntimeException(sprintf('Found unsupported value of type %s during definition injector generation: "%s"', gettype($value), json_encode($value))); + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Generator/LookupMethodClassGenerator.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Generator/LookupMethodClassGenerator.php new file mode 100644 index 0000000..54b02cf --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Generator/LookupMethodClassGenerator.php @@ -0,0 +1,119 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Generator; + +use Metadata\ClassHierarchyMetadata; +use CG\Generator\PhpParameter; +use CG\Generator\PhpMethod; +use CG\Generator\PhpProperty; +use CG\Generator\PhpClass; +use CG\Proxy\GeneratorInterface; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; + +class LookupMethodClassGenerator implements GeneratorInterface +{ + const PREFIX = '__jmsDiExtra_'; + + private $metadata; + private $requiredFile; + + public function __construct(ClassHierarchyMetadata $metadata) + { + $this->metadata = $metadata; + } + + public function setRequiredFile($file) + { + $this->requiredFile = $file; + } + + public function generate(\ReflectionClass $class, PhpClass $genClass) + { + if (!empty($this->requiredFile)) { + $genClass->addRequiredFile($this->requiredFile); + } + + $genClass->setProperty(PhpProperty::create() + ->setName(self::PREFIX.'container') + ->setVisibility('private') + ); + + $genClass->setMethod(PhpMethod::create() + ->setName(self::PREFIX.'setContainer') + ->addParameter(PhpParameter::create() + ->setName('container') + ->setType('Symfony\Component\DependencyInjection\ContainerInterface') + ) + ->setBody('$this->'.self::PREFIX.'container = $container;') + ); + + $genClass->addInterfaceName('JMS\DiExtraBundle\DependencyInjection\LookupMethodClassInterface'); + $genClass->setMethod(PhpMethod::create() + ->setName(self::PREFIX.'getOriginalClassName') + ->setFinal(true) + ->setBody('return '.var_export($class->name, true).';') + ); + + foreach ($this->getLookupMethods() as $name => $value) { + $genClass->setMethod(PhpMethod::fromReflection($class->getMethod($name)) + ->setAbstract(false) + ->setBody('return '.$this->dumpValue($value).';') + ->setDocblock(null) + ); + } + } + + private function getLookupMethods() + { + $outerClass = $this->metadata->getOutsideClassMetadata()->reflection; + $lookupMethods = array(); + foreach ($this->metadata->classMetadata as $classMetadata) { + if (!$classMetadata->lookupMethods) { + continue; + } + + foreach ($classMetadata->lookupMethods as $name => $value) { + // check if method has been overridden + if ($outerClass->getMethod($name)->class !== $classMetadata->reflection->name) { + continue; + } + + $lookupMethods[$name] = $value; + } + } + + return $lookupMethods; + } + + private function dumpValue($value) + { + if ($value instanceof Parameter) { + return '$this->'.self::PREFIX.'container->getParameter('.var_export((string) $value, true).')'; + } else if ($value instanceof Reference) { + return '$this->'.self::PREFIX.'container->get('.var_export((string) $value, true).', '.var_export($value->getInvalidBehavior(), true).')'; + } else if (is_string($value) && '%' === $value[0]) { + return '$this->'.self::PREFIX.'container->getParameter('.var_export(substr($value, 1, -1), true).')'; + } else if (is_array($value) || is_scalar($value) || null === $value) { + return var_export($value, true); + } + + throw new \RuntimeException(sprintf('Invalid value for lookup method: %s', json_encode($value))); + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Generator/NameGenerator.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Generator/NameGenerator.php new file mode 100644 index 0000000..df3c1c8 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Generator/NameGenerator.php @@ -0,0 +1,79 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Generator; + +class NameGenerator +{ + private $count = 0; + private $firstChars = 'abcdefghijklmnopqrstuvwxyz'; + private $firstCharsLength = 26; + private $nonFirstChars = 'abcdefghijklmnopqrstuvwxyz0123456789_'; + private $nonFirstCharsLength = 37; + private $reservedNames = array(); + + public function addReservedName($name) + { + $this->reservedNames[$name] = true; + } + + public function setFirstChars($chars) + { + $this->firstChars = $chars; + $this->firstCharsLength = strlen($chars); + } + + public function setNonFirstChars($chars) + { + $this->nonFirstChars = $chars; + $this->nonFirstCharsLength = strlen($chars); + } + + public function reset() + { + $this->count = 0; + } + + public function nextName() + { + while (true) { + $name = ''; + $i = $this->count; + + if ('' === $name) { + $name .= $this->firstChars[$i%$this->firstCharsLength]; + $i = intval($i/$this->firstCharsLength); + } + + while ($i > 0) { + $i -= 1; + $name .= $this->nonFirstChars[$i%$this->nonFirstCharsLength]; + $i = intval($i/$this->nonFirstCharsLength); + } + + $this->count += 1; + + // check that the name is not reserved + if (isset($this->reservedNames[$name])) { + continue; + } + + return $name; + } + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/HttpKernel/ControllerResolver.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/HttpKernel/ControllerResolver.php new file mode 100644 index 0000000..f1a48ce --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/HttpKernel/ControllerResolver.php @@ -0,0 +1,179 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\HttpKernel; + +use Metadata\ClassHierarchyMetadata; +use JMS\DiExtraBundle\Metadata\ClassMetadata; +use CG\Core\DefaultNamingStrategy; +use CG\Proxy\Enhancer; +use JMS\AopBundle\DependencyInjection\Compiler\PointcutMatchingPass; +use JMS\DiExtraBundle\Generator\DefinitionInjectorGenerator; +use JMS\DiExtraBundle\Generator\LookupMethodClassGenerator; +use JMS\DiExtraBundle\DependencyInjection\Dumper\PhpDumper; +use Metadata\MetadataFactory; +use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\ConfigCache; +use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver as BaseControllerResolver; + +class ControllerResolver extends BaseControllerResolver +{ + protected function createController($controller) + { + if (false === $pos = strpos($controller, '::')) { + $count = substr_count($controller, ':'); + if (2 == $count) { + // controller in the a:b:c notation then + $controller = $this->parser->parse($controller); + $pos = strpos($controller, '::'); + } elseif (1 == $count) { + // controller in the service:method notation + list($service, $method) = explode(':', $controller); + + return array($this->container->get($service), $method); + } else { + throw new \LogicException(sprintf('Unable to parse the controller name "%s".', $controller)); + } + } + + $class = substr($controller, 0, $pos); + $method = substr($controller, $pos+2); + + if (!class_exists($class)) { + throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class)); + } + + $filename = $this->container->getParameter('jms_di_extra.cache_dir').'/controller_injectors/'.str_replace('\\', '', $class).'.php'; + $cache = new ConfigCache($filename, $this->container->getParameter('kernel.debug')); + + if (!$cache->isFresh()) { + $metadata = $this->container->get('jms_di_extra.metadata.metadata_factory')->getMetadataForClass($class); + if (null === $metadata) { + $metadata = new ClassHierarchyMetadata(); + $metadata->addClassMetadata(new ClassMetadata($class)); + } + + $this->prepareContainer($cache, $filename, $metadata); + } + + $inject = require $filename; + $controller = $inject($this->container); + + if ($controller instanceof ContainerAwareInterface) { + $controller->setContainer($this->container); + } + + return array($controller, $method); + } + + private function prepareContainer($cache, $containerFilename, $metadata) + { + $container = new ContainerBuilder(); + $container->setParameter('jms_aop.cache_dir', $this->container->getParameter('jms_di_extra.cache_dir')); + $def = $container + ->register('jms_aop.interceptor_loader', 'JMS\AopBundle\Aop\InterceptorLoader') + ->addArgument(new Reference('service_container')) + ->setPublic(false) + ; + + // add resources + $ref = $metadata->getOutsideClassMetadata()->reflection; + while ($ref && false !== $filename = $ref->getFilename()) { + $container->addResource(new FileResource($filename)); + $ref = $ref->getParentClass(); + } + + // add definitions + $definitions = $this->container->get('jms_di_extra.metadata.converter')->convert($metadata); + $serviceIds = $parameters = array(); + + $controllerDef = array_pop($definitions); + $container->setDefinition('controller', $controllerDef); + + foreach ($definitions as $id => $def) { + $container->setDefinition($id, $def); + } + + $this->generateLookupMethods($controllerDef, $metadata); + + $config = $container->getCompilerPassConfig(); + $config->setOptimizationPasses(array()); + $config->setRemovingPasses(array()); + $config->addPass(new ResolveDefinitionTemplatesPass()); + $config->addPass(new PointcutMatchingPass($this->container->get('jms_aop.pointcut_container')->getPointcuts())); + $config->addPass(new InlineServiceDefinitionsPass()); + $container->compile(); + + if (!file_exists($dir = dirname($containerFilename))) { + if (false === @mkdir($dir, 0777, true)) { + throw new \RuntimeException(sprintf('Could not create directory "%s".', $dir)); + } + } + + static $generator; + if (null === $generator) { + $generator = new DefinitionInjectorGenerator(); + } + + $cache->write($generator->generate($container->getDefinition('controller')), $container->getResources()); + } + + private function generateLookupMethods($def, $metadata) + { + $found = false; + foreach ($metadata->classMetadata as $cMetadata) { + if (!empty($cMetadata->lookupMethods)) { + $found = true; + break; + } + } + + if (!$found) { + return; + } + + $generator = new LookupMethodClassGenerator($metadata); + $outerClass = $metadata->getOutsideClassMetadata()->reflection; + + if ($file = $def->getFile()) { + $generator->setRequiredFile($file); + } + + $enhancer = new Enhancer( + $outerClass, + array(), + array( + $generator, + ) + ); + + $filename = $this->container->getParameter('jms_di_extra.cache_dir').'/lookup_method_classes/'.str_replace('\\', '-', $outerClass->name).'.php'; + $enhancer->writeClass($filename); + + $def->setFile($filename); + $def->setClass($enhancer->getClassName($outerClass)); + $def->addMethodCall('__jmsDiExtra_setContainer', array(new Reference('service_container'))); + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/JMSDiExtraBundle.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/JMSDiExtraBundle.php new file mode 100644 index 0000000..6a88dbe --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/JMSDiExtraBundle.php @@ -0,0 +1,50 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle; + +use JMS\DiExtraBundle\DependencyInjection\Compiler\ResourceOptimizationPass; +use JMS\DiExtraBundle\DependencyInjection\Compiler\IntegrationPass; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use JMS\DiExtraBundle\DependencyInjection\Compiler\AnnotationConfigurationPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class JMSDiExtraBundle extends Bundle +{ + const VERSION = '1.0.1'; + + private $kernel; + + public function __construct(KernelInterface $kernel) + { + $this->kernel = $kernel; + } + + public function build(ContainerBuilder $container) + { + $config = $container->getCompiler()->getPassConfig(); + $passes = $config->getBeforeOptimizationPasses(); + array_unshift($passes, new AnnotationConfigurationPass($this->kernel)); + $config->setBeforeOptimizationPasses($passes); + + $container->addCompilerPass(new IntegrationPass()); + $container->addCompilerPass(new ResourceOptimizationPass(), PassConfig::TYPE_AFTER_REMOVING); + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Metadata/ClassMetadata.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Metadata/ClassMetadata.php new file mode 100644 index 0000000..7b992d9 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Metadata/ClassMetadata.php @@ -0,0 +1,74 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Metadata; + +use Metadata\ClassMetadata as BaseClassMetadata; + +class ClassMetadata extends BaseClassMetadata +{ + public $id; + public $parent; + public $scope; + public $public; + public $abstract; + public $tags = array(); + public $arguments; + public $methodCalls = array(); + public $lookupMethods = array(); + public $properties = array(); + public $initMethod; + + public function serialize() + { + return serialize(array( + $this->id, + $this->parent, + $this->scope, + $this->public, + $this->abstract, + $this->tags, + $this->arguments, + $this->methodCalls, + $this->lookupMethods, + $this->properties, + $this->initMethod, + parent::serialize(), + )); + } + + public function unserialize($str) + { + list( + $this->id, + $this->parent, + $this->scope, + $this->public, + $this->abstract, + $this->tags, + $this->arguments, + $this->methodCalls, + $this->lookupMethods, + $this->properties, + $this->initMethod, + $parentStr + ) = unserialize($str); + + parent::unserialize($parentStr); + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Metadata/Driver/AnnotationDriver.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Metadata/Driver/AnnotationDriver.php new file mode 100644 index 0000000..cbd2051 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Metadata/Driver/AnnotationDriver.php @@ -0,0 +1,220 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Metadata\Driver; + +use JMS\DiExtraBundle\Annotation\AfterSetup; + +use JMS\DiExtraBundle\Annotation\FormType; + +use JMS\DiExtraBundle\Annotation\DoctrineListener; +use JMS\DiExtraBundle\Annotation\Reference as AnnotReference; +use JMS\DiExtraBundle\Annotation\LookupMethod; +use JMS\DiExtraBundle\Annotation\Validator; +use JMS\DiExtraBundle\Annotation\InjectParams; +use JMS\DiExtraBundle\Exception\InvalidTypeException; +use JMS\DiExtraBundle\Annotation\Observe; +use Doctrine\Common\Annotations\Reader; +use JMS\DiExtraBundle\Annotation\Inject; +use JMS\DiExtraBundle\Annotation\Service; +use JMS\DiExtraBundle\Annotation\Tag; +use JMS\DiExtraBundle\Metadata\ClassMetadata; +use Metadata\Driver\DriverInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Reference; + +class AnnotationDriver implements DriverInterface +{ + private $reader; + + public function __construct(Reader $reader) + { + $this->reader = $reader; + } + + public function loadMetadataForClass(\ReflectionClass $class) + { + $metadata = new ClassMetadata($className = $class->getName()); + if (false !== $filename = $class->getFilename()) { + $metadata->fileResources[] = $filename; + } + + // this is a bit of a hack, but avoids any timeout issues when a class + // is moved into one of the compiled classes files, and Doctrine + // Common 2.1 is used. + if (false !== strpos($filename, '/classes.php') + || false !== strpos($filename, '/bootstrap.php')) { + return null; + } + + foreach ($this->reader->getClassAnnotations($class) as $annot) { + if ($annot instanceof Service) { + if (null === $annot->id) { + $metadata->id = $this->generateId($className); + } else { + $metadata->id = $annot->id; + } + + $metadata->parent = $annot->parent; + $metadata->public = $annot->public; + $metadata->scope = $annot->scope; + $metadata->abstract = $annot->abstract; + } else if ($annot instanceof Tag) { + $metadata->tags[$annot->name][] = $annot->attributes; + } else if ($annot instanceof Validator) { + // automatically register as service if not done explicitly + if (null === $metadata->id) { + $metadata->id = $this->generateId($className); + } + + $metadata->tags['validator.constraint_validator'][] = array( + 'alias' => $annot->alias, + ); + } else if ($annot instanceof DoctrineListener) { + if (null === $metadata->id) { + $metadata->id = $this->generateId($className); + } + + foreach ($annot->events as $event) { + $metadata->tags['doctrine.event_listener'][] = array( + 'event' => $event, + 'connection' => $annot->connection ?: 'default', + 'lazy' => $annot->lazy, + 'priority' => $annot->priority, + ); + } + } else if ($annot instanceof FormType) { + if (null === $metadata->id) { + $metadata->id = $this->generateId($className); + } + + $alias = $annot->alias; + + // try to extract it from the class itself + if (null === $alias) { + $instance = unserialize(sprintf('O:%d:"%s":0:{}', strlen($className), $className)); + $alias = $instance->getName(); + } + + $metadata->tags['form.type'][] = array( + 'alias' => $alias, + ); + } + } + + $hasInjection = false; + foreach ($class->getProperties() as $property) { + if ($property->getDeclaringClass()->getName() !== $className) { + continue; + } + $name = $property->getName(); + + foreach ($this->reader->getPropertyAnnotations($property) as $annot) { + if ($annot instanceof Inject) { + $hasInjection = true; + $metadata->properties[$name] = $this->convertReferenceValue($name, $annot); + } + } + } + + foreach ($class->getMethods() as $method) { + if ($method->getDeclaringClass()->getName() !== $className) { + continue; + } + $name = $method->getName(); + + foreach ($this->reader->getMethodAnnotations($method) as $annot) { + if ($annot instanceof Observe) { + $metadata->tags['kernel.event_listener'][] = array( + 'event' => $annot->event, + 'method' => $name, + 'priority' => $annot->priority, + ); + } else if ($annot instanceof InjectParams) { + $params = array(); + foreach ($method->getParameters() as $param) { + if (!isset($annot->params[$paramName = $param->getName()])) { + $params[] = $this->convertReferenceValue($paramName, new Inject(array('value' => null))); + continue; + } + + $params[] = $this->convertReferenceValue($paramName, $annot->params[$paramName]); + } + + if (!$params) { + continue; + } + + $hasInjection = true; + + if ('__construct' === $name) { + $metadata->arguments = $params; + } else { + $metadata->methodCalls[] = array($name, $params); + } + } else if ($annot instanceof LookupMethod) { + $hasInjection = true; + + if ($method->isFinal()) { + throw new \RuntimeException(sprintf('The method "%s::%s" is marked as final and cannot be declared as lookup-method.', $className, $name)); + } + if ($method->isPrivate()) { + throw new \RuntimeException(sprintf('The method "%s::%s" is marked as private and cannot be declared as lookup-method.', $className, $name)); + } + if ($method->getParameters()) { + throw new \RuntimeException(sprintf('The method "%s::%s" must have a no-arguments signature if you want to use it as lookup-method.', $className, $name)); + } + + $metadata->lookupMethods[$name] = $this->convertReferenceValue('get' === substr($name, 0, 3) ? substr($name, 3) : $name, $annot); + } else if ($annot instanceof AfterSetup) { + if (!$method->isPublic()) { + throw new \RuntimeException(sprintf('The init method "%s::%s" must be public.', $method->class, $method->name)); + } + + $metadata->initMethod = $method->name; + } + } + } + + if (null == $metadata->id && !$hasInjection) { + return null; + } + + return $metadata; + } + + private function convertReferenceValue($name, AnnotReference $annot) + { + if (null === $annot->value) { + return new Reference($this->generateId($name), false !== $annot->required ? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE : ContainerInterface::NULL_ON_INVALID_REFERENCE); + } + + if (false === strpos($annot->value, '%')) { + return new Reference($annot->value, false !== $annot->required ? ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE : ContainerInterface::NULL_ON_INVALID_REFERENCE); + } + + return $annot->value; + } + + private function generateId($name) + { + $name = preg_replace('/(?<=[a-zA-Z0-9])[A-Z]/', '_\\0', $name); + + return strtolower(strtr($name, '\\', '.')); + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Metadata/MetadataConverter.php b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Metadata/MetadataConverter.php new file mode 100644 index 0000000..a4f8ea5 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Metadata/MetadataConverter.php @@ -0,0 +1,85 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\DiExtraBundle\Metadata; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Definition; +use Metadata\ClassHierarchyMetadata; + +class MetadataConverter +{ + /** + * Converts class hierarchy metadata to definition instances. + * + * @param ClassHierarchyMetadata $metadata + * @return array an array of Definition instances + */ + public function convert(ClassHierarchyMetadata $metadata) + { + static $count = 0; + $definitions = array(); + + $previous = null; + foreach ($metadata->classMetadata as $classMetadata) { + if (null === $previous && null === $classMetadata->parent) { + $definition = new Definition(); + } else { + $definition = new DefinitionDecorator( + $classMetadata->parent ?: $previous->id + ); + } + + $definition->setClass($classMetadata->name); + if (null !== $classMetadata->scope) { + $definition->setScope($classMetadata->scope); + } + if (null !== $classMetadata->public) { + $definition->setPublic($classMetadata->public); + } + if (null !== $classMetadata->abstract) { + $definition->setAbstract($classMetadata->abstract); + } + if (null !== $classMetadata->arguments) { + $definition->setArguments($classMetadata->arguments); + } + + $definition->setMethodCalls($classMetadata->methodCalls); + $definition->setTags($classMetadata->tags); + $definition->setProperties($classMetadata->properties); + + if (null === $classMetadata->id) { + $classMetadata->id = '_jms_di_extra.unnamed.service_'.$count++; + } + + if ($classMetadata->initMethod) { + if (!method_exists($definition, 'setInitMethod')) { + throw new \RuntimeException(sprintf('@AfterSetup is not available on your Symfony version.')); + } + + $definition->setInitMethod($classMetadata->initMethod); + } + + $definitions[$classMetadata->id] = $definition; + $previous = $classMetadata; + } + + return $definitions; + } +} \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/README b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/README new file mode 100644 index 0000000..72bab21 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/README @@ -0,0 +1,8 @@ +For documentation, see: + + Resources/doc + + +For license, see: + + Resources/meta/LICENSE \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Resources/config/services.xml b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Resources/config/services.xml new file mode 100644 index 0000000..c1ac0d4 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Resources/config/services.xml @@ -0,0 +1,50 @@ + + + + + + JMS\DiExtraBundle\Metadata\Driver\AnnotationDriver + + Metadata\MetadataFactory + Metadata\Cache\FileCache + + JMS\DiExtraBundle\Metadata\MetadataConverter + JMS\DiExtraBundle\HttpKernel\ControllerResolver + + JMS\DiExtraBundle\EventListener\TemplateListener + JMS\DiExtraBundle\EventListener\ControllerListener + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Resources/doc/index.rst b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Resources/doc/index.rst new file mode 100644 index 0000000..9b7ece5 --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Resources/doc/index.rst @@ -0,0 +1,290 @@ +======== +Overview +======== + +This bundle allows you to configure dependency injection using annotations. + +Installation +------------ +Add the following to your ``deps`` file:: + + [JMSDiExtraBundle] + git=https://github.com/schmittjoh/JMSDiExtraBundle.git + target=/bundles/JMS/DiExtraBundle + + ; Dependencies: + ;-------------- + [metadata] + git=https://github.com/schmittjoh/metadata.git + version=1.1.0 ; <- make sure to get 1.1, not 1.0 + +Then register the bundle with your kernel:: + + // in AppKernel::registerBundles() + $bundles = array( + // ... + new JMS\DiExtraBundle\JMSDiExtraBundle($this), + // ... + ); + +In addition, this bundle also requires the JMSAopBundle. See its documentation for +installation instructions:: + + https://github.com/schmittjoh/JMSAopBundle/blob/master/Resources/doc/index.rst + + +Make sure that you also register the namespaces with the autoloader:: + + // app/autoload.php + $loader->registerNamespaces(array( + // ... + 'JMS' => __DIR__.'/../vendor/bundles', + 'Metadata' => __DIR__.'/../vendor/metadata/src', + // ... + )); + + +Configuration +------------- +For optimal development performance (in production there is no difference either way), +it is recommended to explicitly configure the directories which should be scanned for +service classes (by default no directory is scanned):: + + jms_di_extra: + locations: + all_bundles: false + bundles: [FooBundle, AcmeBlogBundle, etc.] + directories: [%kernel.root_dir%/../src, some/other/dir] + +Usage +----- + +Non-Controller Classes +~~~~~~~~~~~~~~~~~~~~~~ + +Non-controller classes are configured, and managed by Symfony's DIC just like any +other service that you configure using YML, XML, or PHP. The only difference is +that you can do it via annotations which is a lot more convenient. + +You can use these annotations on services (for examples, see below): +@Service, @Inject, @InjectParams, @Observe, @Tag + +Note that you cannot use the @Inject annotation on private, or protected properties. +Likewise, the @InjectParams annotation does not work on protected, or private methods. + + +Controllers +~~~~~~~~~~~ + +Controllers are a special type of class which is also treated specially by this +bundle. The most notable difference is that you do not need to define these +classes as services. Yes, no services, but don't worry you can still use all of +the DIC's features, and even some more. + +- Constructor/Setter Injection:: + + em = $em; + $this->session = $session; + } + // ... some actions + } + + **Note:** Constructor Injection is not possible when a parent definition + also defines a constructor which is configured for injection. + +- Property Injection:: + + getMailer(); + } + } + + /** @DI\LookupMethod("mailer") */ + protected function getMailer() { /* empty body here */ } + } + +You can use this type of injection if you have a dependency that you do not +always need in the controller, and which is costly to initialize, like the +mailer in the example above. + + +Annotations +----------- + +@Inject +~~~~~~~~~ +This marks a property, or parameter for injection:: + + use JMS\DiExtraBundle\Annotation\Inject; + + class Controller + { + /** + * @Inject("security.context", required = false) + */ + private $securityContext; + + /** + * @Inject("%kernel.cache_dir%") + */ + private $cacheDir; + + /** + * @Inject + */ + private $session; + } + +If you do not specify the service explicitly, we will try to guess it based on the name +of the property or the parameter. + +@InjectParams +~~~~~~~~~~~~~~~ +This marks the parameters of a method for injection:: + + use JMS\DiExtraBundle\Annotation\Inject; + use JMS\DiExtraBundle\Annotation\InjectParams; + use JMS\DiExtraBundle\Annotation\Service; + + /** + * @Service + */ + class Listener + { + /** + * @InjectParams({ + * "em" = @Inject("doctrine.entity_manager") + * }) + */ + public function __construct(EntityManager $em, Session $session) + { + // ... + } + } + +If you don't define all parameters in the param map, we will try to guess which services +should be injected into the remaining parameters based on their name. + +@Service +~~~~~~~~ +Marks a class as service:: + + use JMS\DiExtraBundle\Annotation\Service; + + /** + * @Service("some.service.id", parent="another.service.id", public=false) + */ + class Listener + { + } + +If you do not explicitly define a service id, then we will generated a sensible default +based on the fully qualified class name for you. + +@Tag +~~~~ +Adds a tag to the service:: + + use JMS\DiExtraBundle\Annotation\Service; + use JMS\DiExtraBundle\Annotation\Tag; + + /** + * @Service + * @Tag("doctrine.event_listener", attributes = {"event" = "postGenerateSchema", lazy=true}) + */ + class Listener + { + // ... + } + +@Observe +~~~~~~~~ +Automatically registers a method as listener to a certain event:: + + use JMS\DiExtraBundle\Annotation\Observe; + use JMS\DiExtraBundle\Annotation\Service; + + /** + * @Service + */ + class RequestListener + { + /** + * @Observe("kernel.request", priority = 255) + */ + public function onKernelRequest() + { + // ... + } + } + +@Validator +~~~~~~~~~~ +Automatically registers the given class as constraint validator for the Validator component:: + + use JMS\DiExtraBundle\Annotation\Validator; + use Symfony\Component\Validator\Constraint; + use Symfony\Component\Validator\ConstraintValidator; + + /** + * @Validator("my_alias") + */ + class MyValidator extends ConstraintValidator + { + // ... + } + + class MyConstraint extends Constraint + { + // ... + public function validatedBy() + { + return 'my_alias'; + } + } + +The @Validator annotation also implies the @Service annotation if you do not specify it explicitly. +The alias which is passed to the @Validator annotation must match the string that is returned from +the ``validatedBy`` method of your constraint. + diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Resources/meta/LICENSE b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Resources/meta/LICENSE new file mode 100644 index 0000000..753842b --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/Resources/meta/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/composer.json b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/composer.json new file mode 100644 index 0000000..cc6196c --- /dev/null +++ b/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/composer.json @@ -0,0 +1,21 @@ +{ + "name": "jms/di-extra-bundle", + "description": "Allows to configure dependency injection using annotations", + "keywords": ["annotations","dependency injection"], + "type": "symfony-bundle", + "license": "Apache", + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "require": { + "symfony/framework-bundle": "2.*", + "jms/metadata": "1.1.*" + }, + "autoload": { + "psr-0": { "JMS\\DiExtraBundle": "" } + }, + "target-dir": "JMS/DiExtraBundle" +} diff --git a/vendor/jms/metadata/CHANGELOG.md b/vendor/jms/metadata/CHANGELOG.md new file mode 100644 index 0000000..74da57e --- /dev/null +++ b/vendor/jms/metadata/CHANGELOG.md @@ -0,0 +1,18 @@ +CHANGELOG +========= + +This changelog references all relevant changes: + +To get the diff between the two last versions, go to +https://github.com/schmittjoh/metadata/compare/1.0.0...1.1.0 + +* 1.1.0 (2011-10-04) + + * added support for metadata on interfaces + * added support for non annotation-based drivers + * added support for merging metadata + +This release is fully backwards compatible with the 1.0.0 release. Therefore, +the 1.0.x branch has been discontinued. + +* 1.0.0 (2011-07-09) diff --git a/vendor/jms/metadata/LICENSE b/vendor/jms/metadata/LICENSE new file mode 100644 index 0000000..f49a4e1 --- /dev/null +++ b/vendor/jms/metadata/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/vendor/jms/metadata/composer.json b/vendor/jms/metadata/composer.json new file mode 100644 index 0000000..e598e25 --- /dev/null +++ b/vendor/jms/metadata/composer.json @@ -0,0 +1,19 @@ +{ + "name": "jms/metadata", + "description": "Class/method/property metadata management in PHP", + "keywords": ["annotations","metadata","yaml","xml"], + "type": "library", + "license": "Apache", + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-0": { "Metadata\\": "src/" } + } +} diff --git a/vendor/jms/metadata/src/Metadata/Cache/CacheInterface.php b/vendor/jms/metadata/src/Metadata/Cache/CacheInterface.php new file mode 100644 index 0000000..dbf3ccb --- /dev/null +++ b/vendor/jms/metadata/src/Metadata/Cache/CacheInterface.php @@ -0,0 +1,35 @@ +dir = rtrim($dir, '\\/'); + } + + public function loadClassMetadataFromCache(\ReflectionClass $class) + { + $path = $this->dir.'/'.strtr($class->getName(), '\\', '-').'.cache.php'; + if (!file_exists($path)) { + return null; + } + + return include $path; + } + + public function putClassMetadataInCache(ClassMetadata $metadata) + { + $path = $this->dir.'/'.strtr($metadata->name, '\\', '-').'.cache.php'; + file_put_contents($path, 'dir.'/'.strtr($class->getName(), '\\', '-').'.cache.php'; + if (file_exists($path)) { + unlink($path); + } + } +} \ No newline at end of file diff --git a/vendor/jms/metadata/src/Metadata/ClassHierarchyMetadata.php b/vendor/jms/metadata/src/Metadata/ClassHierarchyMetadata.php new file mode 100644 index 0000000..c90a5e4 --- /dev/null +++ b/vendor/jms/metadata/src/Metadata/ClassHierarchyMetadata.php @@ -0,0 +1,55 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Metadata; + +/** + * Represents the metadata for the entire class hierarchy. + * + * @author Johannes M. Schmitt + */ +class ClassHierarchyMetadata +{ + public $classMetadata = array(); + + public function addClassMetadata(ClassMetadata $metadata) + { + $this->classMetadata[$metadata->name] = $metadata; + } + + public function getRootClassMetadata() + { + return reset($this->classMetadata); + } + + public function getOutsideClassMetadata() + { + return end($this->classMetadata); + } + + public function isFresh($timestamp) + { + foreach ($this->classMetadata as $metadata) { + if (!$metadata->isFresh($timestamp)) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/vendor/jms/metadata/src/Metadata/ClassMetadata.php b/vendor/jms/metadata/src/Metadata/ClassMetadata.php new file mode 100644 index 0000000..2479a4f --- /dev/null +++ b/vendor/jms/metadata/src/Metadata/ClassMetadata.php @@ -0,0 +1,98 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Metadata; + +/** + * Base class for class metadata. + * + * This class is intended to be extended to add your own application specific + * properties, and flags. + * + * @author Johannes M. Schmitt + */ +class ClassMetadata implements \Serializable +{ + public $name; + public $reflection; + public $methodMetadata = array(); + public $propertyMetadata = array(); + public $fileResources = array(); + public $createdAt; + + public function __construct($name) + { + $this->name = $name; + + $this->reflection = new \ReflectionClass($name); + $this->createdAt = time(); + } + + public function addMethodMetadata(MethodMetadata $metadata) + { + $this->methodMetadata[$metadata->name] = $metadata; + } + + public function addPropertyMetadata(PropertyMetadata $metadata) + { + $this->propertyMetadata[$metadata->name] = $metadata; + } + + public function isFresh($timestamp = null) + { + if (null === $timestamp) { + $timestamp = $this->createdAt; + } + + foreach ($this->fileResources as $filepath) { + if (!file_exists($filepath)) { + return false; + } + + if ($timestamp < filemtime($filepath)) { + return false; + } + } + + return true; + } + + public function serialize() + { + return serialize(array( + $this->name, + $this->methodMetadata, + $this->propertyMetadata, + $this->fileResources, + $this->createdAt, + )); + } + + public function unserialize($str) + { + list( + $this->name, + $this->methodMetadata, + $this->propertyMetadata, + $this->fileResources, + $this->createdAt + ) = unserialize($str); + + $this->reflection = new \ReflectionClass($this->name); + } +} \ No newline at end of file diff --git a/vendor/jms/metadata/src/Metadata/Driver/AbstractFileDriver.php b/vendor/jms/metadata/src/Metadata/Driver/AbstractFileDriver.php new file mode 100644 index 0000000..b02f387 --- /dev/null +++ b/vendor/jms/metadata/src/Metadata/Driver/AbstractFileDriver.php @@ -0,0 +1,42 @@ + + */ +abstract class AbstractFileDriver implements DriverInterface +{ + private $locator; + + public function __construct(FileLocatorInterface $locator) + { + $this->locator = $locator; + } + + public function loadMetadataForClass(\ReflectionClass $class) + { + if (null === $path = $this->locator->findFileForClass($class, $this->getExtension())) { + return null; + } + + return $this->loadMetadataFromFile($class, $path); + } + + /** + * Parses the content of the file, and converts it to the desired metadata. + * + * @param string $file + * @return ClassMetadata|null + */ + abstract protected function loadMetadataFromFile(\ReflectionClass $class, $file); + + /** + * Returns the extension of the file. + * + * @return string + */ + abstract protected function getExtension(); +} \ No newline at end of file diff --git a/vendor/jms/metadata/src/Metadata/Driver/DriverChain.php b/vendor/jms/metadata/src/Metadata/Driver/DriverChain.php new file mode 100644 index 0000000..809ccf1 --- /dev/null +++ b/vendor/jms/metadata/src/Metadata/Driver/DriverChain.php @@ -0,0 +1,40 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Metadata\Driver; + +final class DriverChain implements DriverInterface +{ + private $drivers; + + public function __construct(array $drivers) + { + $this->drivers = $drivers; + } + + public function loadMetadataForClass(\ReflectionClass $class) + { + foreach ($this->drivers as $driver) { + if (null !== $metadata = $driver->loadMetadataForClass($class)) { + return $metadata; + } + } + + return null; + } +} \ No newline at end of file diff --git a/vendor/jms/metadata/src/Metadata/Driver/DriverInterface.php b/vendor/jms/metadata/src/Metadata/Driver/DriverInterface.php new file mode 100644 index 0000000..57ed66d --- /dev/null +++ b/vendor/jms/metadata/src/Metadata/Driver/DriverInterface.php @@ -0,0 +1,24 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Metadata\Driver; + +interface DriverInterface +{ + function loadMetadataForClass(\ReflectionClass $class); +} diff --git a/vendor/jms/metadata/src/Metadata/Driver/FileLocator.php b/vendor/jms/metadata/src/Metadata/Driver/FileLocator.php new file mode 100644 index 0000000..41f7b99 --- /dev/null +++ b/vendor/jms/metadata/src/Metadata/Driver/FileLocator.php @@ -0,0 +1,29 @@ +dirs = $dirs; + } + + public function findFileForClass(\ReflectionClass $class, $extension) + { + foreach ($this->dirs as $prefix => $dir) { + if (0 !== strpos($class->getNamespaceName(), $prefix)) { + continue; + } + + $path = $dir.'/'.str_replace('\\', '.', substr($class->getName(), strlen($prefix)+1)).'.'.$extension; + if (file_exists($path)) { + return $path; + } + } + + return null; + } +} \ No newline at end of file diff --git a/vendor/jms/metadata/src/Metadata/Driver/FileLocatorInterface.php b/vendor/jms/metadata/src/Metadata/Driver/FileLocatorInterface.php new file mode 100644 index 0000000..5092f3d --- /dev/null +++ b/vendor/jms/metadata/src/Metadata/Driver/FileLocatorInterface.php @@ -0,0 +1,8 @@ +container = $container; + $this->realDriverId = $realDriverId; + } + + public function loadMetadataForClass(\ReflectionClass $class) + { + return $this->container->get($this->realDriverId)->loadMetadataForClass($class); + } +} \ No newline at end of file diff --git a/vendor/jms/metadata/src/Metadata/MergeableClassMetadata.php b/vendor/jms/metadata/src/Metadata/MergeableClassMetadata.php new file mode 100644 index 0000000..13ec158 --- /dev/null +++ b/vendor/jms/metadata/src/Metadata/MergeableClassMetadata.php @@ -0,0 +1,23 @@ +name = $object->name; + $this->reflection = $object->reflection; + $this->methodMetadata = array_merge($this->methodMetadata, $object->methodMetadata); + $this->propertyMetadata = array_merge($this->propertyMetadata, $object->propertyMetadata); + $this->fileResources = array_merge($this->fileResources, $object->fileResources); + + if ($object->createdAt < $this->createdAt) { + $this->createdAt = $object->createdAt; + } + } +} \ No newline at end of file diff --git a/vendor/jms/metadata/src/Metadata/MergeableInterface.php b/vendor/jms/metadata/src/Metadata/MergeableInterface.php new file mode 100644 index 0000000..1da234f --- /dev/null +++ b/vendor/jms/metadata/src/Metadata/MergeableInterface.php @@ -0,0 +1,8 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Metadata; + +use Metadata\Driver\DriverInterface; +use Metadata\Cache\CacheInterface; + +final class MetadataFactory implements MetadataFactoryInterface +{ + private $driver; + private $cache; + private $loadedMetadata = array(); + private $loadedClassMetadata = array(); + private $hierarchyMetadataClass; + private $includeInterfaces = false; + private $debug; + + public function __construct(DriverInterface $driver, $hierarchyMetadataClass = 'Metadata\ClassHierarchyMetadata', $debug = false) + { + $this->driver = $driver; + $this->hierarchyMetadataClass = $hierarchyMetadataClass; + $this->debug = $debug; + } + + public function setIncludeInterfaces($bool) + { + $this->includeInterfaces = (Boolean) $bool; + } + + public function setCache(CacheInterface $cache) + { + $this->cache = $cache; + } + + public function getMetadataForClass($className) + { + if (isset($this->loadedMetadata[$className])) { + return $this->loadedMetadata[$className]; + } + + $metadata = null; + foreach ($this->getClassHierarchy($className) as $class) { + if (isset($this->loadedClassMetadata[$name = $class->getName()])) { + $this->addClassMetadata($metadata, $this->loadedClassMetadata[$name]); + continue; + } + + // check the cache + if (null !== $this->cache + && (null !== $classMetadata = $this->cache->loadClassMetadataFromCache($class))) { + if ($this->debug && !$classMetadata->isFresh()) { + $this->cache->evictClassMetadataFromCache($classMetadata->reflection); + } else { + $this->loadedClassMetadata[$name] = $classMetadata; + $this->addClassMetadata($metadata, $classMetadata); + continue; + } + } + + // load from source + if (null !== $classMetadata = $this->driver->loadMetadataForClass($class)) { + $this->loadedClassMetadata[$name] = $classMetadata; + $this->addClassMetadata($metadata, $classMetadata); + + if (null !== $this->cache) { + $this->cache->putClassMetadataInCache($classMetadata); + } + + continue; + } + } + + return $this->loadedMetadata[$className] = $metadata; + } + + private function addClassMetadata(&$metadata, $toAdd) + { + if ($toAdd instanceof MergeableInterface) { + if (null === $metadata) { + $metadata = clone $toAdd; + } else { + $metadata->merge($toAdd); + } + } else { + if (null === $metadata) { + $metadata = new $this->hierarchyMetadataClass; + } + + $metadata->addClassMetadata($toAdd); + } + } + + private function getClassHierarchy($class) + { + $classes = array(); + $refl = new \ReflectionClass($class); + + do { + $classes[] = $refl; + } while (false !== $refl = $refl->getParentClass()); + + $classes = array_reverse($classes, false); + + if (!$this->includeInterfaces) { + return $classes; + } + + $addedInterfaces = array(); + $newHierarchy = array(); + + foreach ($classes as $class) { + foreach ($class->getInterfaces() as $interface) { + if (isset($addedInterfaces[$interface->getName()])) { + continue; + } + $addedInterfaces[$interface->getName()] = true; + + $newHierarchy[] = $interface; + } + + $newHierarchy[] = $class; + } + + return $newHierarchy; + } +} \ No newline at end of file diff --git a/vendor/jms/metadata/src/Metadata/MetadataFactoryInterface.php b/vendor/jms/metadata/src/Metadata/MetadataFactoryInterface.php new file mode 100644 index 0000000..d7fa68a --- /dev/null +++ b/vendor/jms/metadata/src/Metadata/MetadataFactoryInterface.php @@ -0,0 +1,24 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Metadata; + +interface MetadataFactoryInterface +{ + function getMetadataForClass($className); +} \ No newline at end of file diff --git a/vendor/jms/metadata/src/Metadata/MethodMetadata.php b/vendor/jms/metadata/src/Metadata/MethodMetadata.php new file mode 100644 index 0000000..db2d501 --- /dev/null +++ b/vendor/jms/metadata/src/Metadata/MethodMetadata.php @@ -0,0 +1,61 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Metadata; + +/** + * Base class for method metadata. + * + * This class is intended to be extended to add your application specific + * properties, and flags. + * + * @author Johannes M. Schmitt + */ +class MethodMetadata implements \Serializable +{ + public $class; + public $name; + public $reflection; + + public function __construct($class, $name) + { + $this->class = $class; + $this->name = $name; + + $this->reflection = new \ReflectionMethod($class, $name); + $this->reflection->setAccessible(true); + } + + public function invoke($obj, array $args = array()) + { + return $this->reflection->invokeArgs($obj, $args); + } + + public function serialize() + { + return serialize(array($this->class, $this->name)); + } + + public function unserialize($str) + { + list($this->class, $this->name) = unserialize($str); + + $this->reflection = new \ReflectionMethod($this->class, $this->name); + $this->reflection->setAccessible(true); + } +} \ No newline at end of file diff --git a/vendor/jms/metadata/src/Metadata/PropertyMetadata.php b/vendor/jms/metadata/src/Metadata/PropertyMetadata.php new file mode 100644 index 0000000..a581ce0 --- /dev/null +++ b/vendor/jms/metadata/src/Metadata/PropertyMetadata.php @@ -0,0 +1,69 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Metadata; + +/** + * Base class for property metadata. + * + * This class is intended to be extended to add your application specific + * properties, and flags. + * + * @author Johannes M. Schmitt + */ +class PropertyMetadata implements \Serializable +{ + public $class; + public $name; + public $reflection; + + public function __construct($class, $name) + { + $this->class = $class; + $this->name = $name; + + $this->reflection = new \ReflectionProperty($class, $name); + $this->reflection->setAccessible(true); + } + + public function getValue($obj) + { + return $this->reflection->getValue($obj); + } + + public function setValue($obj, $value) + { + $this->reflection->setValue($obj, $value); + } + + public function serialize() + { + return serialize(array( + $this->class, + $this->name, + )); + } + + public function unserialize($str) + { + list($this->class, $this->name) = unserialize($str); + + $this->reflection = new \ReflectionProperty($this->class, $this->name); + $this->reflection->setAccessible(true); + } +} \ No newline at end of file diff --git a/vendor/jms/metadata/src/Metadata/Version.php b/vendor/jms/metadata/src/Metadata/Version.php new file mode 100644 index 0000000..0ea37d6 --- /dev/null +++ b/vendor/jms/metadata/src/Metadata/Version.php @@ -0,0 +1,24 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Metadata; + +final class Version +{ + const VERSION = '1.1.1'; +} diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/PreAuthorize.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/PreAuthorize.php new file mode 100644 index 0000000..0128138 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/PreAuthorize.php @@ -0,0 +1,59 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Annotation; + +use JMS\SecurityExtraBundle\Exception\InvalidArgumentException; + +/** + * Annotation for expression-based access control. + * + * @Annotation + * @Target("METHOD") + * + * @author Johannes M. Schmitt + */ +final class PreAuthorize +{ + /** + * @Required + * @var string + */ + public $expr; + + public function __construct() + { + if (0 === func_num_args()) { + return; + } + $values = func_get_arg(0); + + if (isset($values['value'])) { + $values['expr'] = $values['value']; + } + if (!isset($values['expr'])) { + throw new InvalidArgumentException('The "expr" attribute must be set for annotation @PreAuthorize.'); + } + + if (!is_string($values['expr'])) { + throw new InvalidArgumentException(sprintf('The "expr" attribute of annotation @PreAuthorize must be a string, but got "%s".', gettype($values['expr']))); + } + + $this->expr = $values['expr']; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/RunAs.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/RunAs.php new file mode 100644 index 0000000..94de7a3 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/RunAs.php @@ -0,0 +1,42 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Annotation; + +use JMS\SecurityExtraBundle\Exception\InvalidArgumentException; + +/** + * @Annotation + * @Target("METHOD") + */ +final class RunAs +{ + public $roles; + + public function __construct(array $values) + { + if (isset($values['value'])) { + $values['roles'] = $values['value']; + } + if (!isset($values['roles'])) { + throw new InvalidArgumentException('"roles" must be defined for RunAs annotation.'); + } + + $this->roles = array_map('trim', explode(',', $values['roles'])); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/SatisfiesParentSecurityPolicy.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/SatisfiesParentSecurityPolicy.php new file mode 100644 index 0000000..e087f2f --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/SatisfiesParentSecurityPolicy.php @@ -0,0 +1,81 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Annotation; + +/** + * This must be declared on classes which inherit from classes that have + * requested method invocation securing capabilities. + * + * It indicates to the analyzer that the developer is aware of these security + * restrictions, and has applied them to the root class in an appropriate + * fashion. + * + * We cannot do this automatically without properly analyzing the control flow, + * and in some cases it is not possible at all. See the following example: + * + * + * // child class + * public function editComment($commentId) + * { + * // retrieve comment from database + * $comment = $this->entityManager->find($commentId); + * + * return parent::editComment($comment); + * } + * + * // base class which is inherited from + * /** + * * @SecureParam(name="comment", permissions="EDIT") + * *\/ + * public function editComment(Comment $comment) + * { + * // do some supposedly secure action + * } + * + * + * The above example can be rewritten so that we can apply security checks + * automatically: + * + * + * // child class + * public function editComment($commentId) + * { + * // retrieve comment from database + * $comment = $this->entityManager->find($commentId); + * + * return $this->doEditComment($comment); + * } + * + * // base class which is inherited from + * /** + * * @SecureParam(name="comment", permissions="EDIT") + * *\/ + * protected function doEditComment(Comment $comment) + * { + * // do some secure action + * } + * + * + * @Annotation + * @Target("METHOD") + * @author Johannes M. Schmitt + */ +final class SatisfiesParentSecurityPolicy +{ +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/Secure.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/Secure.php new file mode 100644 index 0000000..bffd8fd --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/Secure.php @@ -0,0 +1,45 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Annotation; + +use JMS\SecurityExtraBundle\Exception\InvalidArgumentException; + +/** + * Represents a @Secure annotation. + * + * @Annotation + * @Target("METHOD") + * @author Johannes M. Schmitt + */ +final class Secure +{ + public $roles; + + public function __construct(array $values) + { + if (isset($values['value'])) { + $values['roles'] = $values['value']; + } + if (!isset($values['roles'])) { + throw new InvalidArgumentException('You must define a "roles" attribute for each Secure annotation.'); + } + + $this->roles = array_map('trim', explode(',', $values['roles'])); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/SecureParam.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/SecureParam.php new file mode 100644 index 0000000..ec098b7 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/SecureParam.php @@ -0,0 +1,48 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Annotation; + +use JMS\SecurityExtraBundle\Exception\InvalidArgumentException; + +/** + * Represents a @SecureParam annotation. + * + * @Annotation + * @Target("METHOD") + * @author Johannes M. Schmitt + */ +final class SecureParam +{ + public $name; + public $permissions; + + public function __construct(array $values) + { + if (!isset($values['name'])) { + throw new InvalidArgumentException('You must define a "name" attribute for each SecureParam annotation.'); + } + if (!isset($values['permissions'])) { + throw new InvalidArgumentException('You must define a "permissions" attribute for each SecureParam annotation.'); + } + + $this->name = $values['name']; + + $this->permissions = array_map('trim', explode(',', $values['permissions'])); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/SecureReturn.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/SecureReturn.php new file mode 100644 index 0000000..35e071e --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Annotation/SecureReturn.php @@ -0,0 +1,45 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Annotation; + +use JMS\SecurityExtraBundle\Exception\InvalidArgumentException; + +/** + * Represents a @SecureReturn annotation. + * + * @Annotation + * @Target("METHOD") + * @author Johannes M. Schmitt + */ +final class SecureReturn +{ + public $permissions; + + public function __construct(array $values) + { + if (isset($values['value'])) { + $values['permissions'] = $values['value']; + } + if (!isset($values['permissions'])) { + throw new InvalidArgumentException('You must define a "permissions" attribute for each SecureReturn annotation.'); + } + + $this->permissions = array_map('trim', explode(',', $values['permissions'])); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/CHANGELOG-1.1.md b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/CHANGELOG-1.1.md new file mode 100644 index 0000000..a76156b --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/CHANGELOG-1.1.md @@ -0,0 +1,38 @@ +This document details all changes from JMSSecurityExtraBundle 1.0.x to 1.1: + +- The configuration option "secure_controllers" has been removed. This setting is + now automatically enabled, but it requires the JMSDiExtraBundle. + +- The dependencies of this bundle have changed: + + * The metadata library 1.1 version is now required instead of the 1.0 version + (if you are using the Standard Edition, just change the "version=origin/1.0.x" + line from your deps file to "version=1.1.0"). + * The JMSAopBundle is now required. For installation instructions, please see + https://github.com/schmittjoh/JMSAopBundle + * The JMSDiExtraBundle is now required if you want to secure your non-service + controllers (if you only have service controllers, you don't need it). For + installation instructions, see https://github.com/schmittjoh/JMSDiExtraBundle + +- The attribute "IS_IDDQD" has been renamed to "ROLE_IDDQD" + +- A powerful expression-based authorization language has been added which works + in combination with the existing voting system. Since it is much more powerful + than the built-in voters, and also much faster, you are highly encouraged to + migrate your existing authorization rules to expressions, and eventually disable + the built-in voters entirely. Some examples for how to convert simple attributes + to their equivalent expressions are listed below: + + * IS_AUTHENTICATED_ANONYMOUSLY -> "permitAll" + * IS_AUTHENTICATED_REMEMBERED -> "isAuthenticated()" + * IS_AUTHENTICATED_FULLY -> "isFullyAuthenticated()" + * ROLE_FOO -> "hasRole('ROLE_FOO')" + +- The ability to configure method access control (e.g. for controller actions) + in the DI configuration has been added. Note that for non-service controllers + the JMSDiExtraBundle is required. + +- The "is_expr_granted" Twig function has been added if you want to check an + expression from a Twig template. + + diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/AccessControlConfiguration.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/AccessControlConfiguration.php new file mode 100644 index 0000000..dc547a1 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/AccessControlConfiguration.php @@ -0,0 +1,90 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\ConfigurationInterface; + +use Symfony\Component\HttpKernel\Kernel; + +use Symfony\Bundle\SecurityBundle\DependencyInjection\MainConfiguration as BaseConfiguration; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; + +/** + * Enhances the access_control section configuration. + * + * @author Johannes M. Schmitt + */ +class AccessControlConfiguration implements ConfigurationInterface +{ + /** + * Generates the configuration tree builder. + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder + */ + public function getConfigTreeBuilder() + { + $tb = new TreeBuilder(); + $rootNode = $tb->root('security'); + + $rootNode + ->ignoreExtraKeys() + ->fixXmlConfig('rule', 'access_control') + ->children() + ->arrayNode('access_control') + ->cannotBeOverwritten() + ->prototype('array') + ->fixXmlConfig('role') + ->validate() + ->always(function($v) { + if (!empty($v['roles']) && isset($v['access'])) { + throw new \Exception('"roles", and "access" cannot be set at the same time.'); + } + + if (empty($v['roles'])) { + unset($v['roles']); + } + + return $v; + }) + ->end() + ->children() + ->scalarNode('requires_channel')->defaultNull()->end() + ->scalarNode('path')->defaultNull()->end() + ->scalarNode('host')->defaultNull()->end() + ->scalarNode('ip')->defaultNull()->end() + ->arrayNode('methods') + ->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end() + ->prototype('scalar')->end() + ->end() + ->arrayNode('roles') + ->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end() + ->prototype('scalar')->end() + ->end() + ->scalarNode('access')->end() + ->end() + ->end() + ->end() + ->end() + ; + + return $tb; + } +} diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/AddAfterInvocationProvidersPass.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/AddAfterInvocationProvidersPass.php new file mode 100644 index 0000000..64547b1 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/AddAfterInvocationProvidersPass.php @@ -0,0 +1,52 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Collects after invocation providers. + * + * @author Johannes M. Schmitt + */ +class AddAfterInvocationProvidersPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('security.access.after_invocation_manager')) { + return; + } + + $providers = array(); + foreach (array_keys($container->findTaggedServiceIds('security.after_invocation.provider')) as $id) { + if ('security.access.after_invocation.acl_provider' === $id && !$container->has('security.acl.provider')) { + continue; + } + + $providers[] = new Reference($id); + } + + $container + ->getDefinition('security.access.after_invocation_manager') + ->setArguments(array($providers)) + ; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/AddExpressionCompilersPass.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/AddExpressionCompilersPass.php new file mode 100644 index 0000000..6b63a42 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/AddExpressionCompilersPass.php @@ -0,0 +1,66 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\DependencyInjection\Compiler; + +use JMS\SecurityExtraBundle\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +class AddExpressionCompilersPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('security.expressions.compiler')) { + return; + } + + $compilerDef = $container->getDefinition('security.expressions.compiler'); + foreach ($container->findTaggedServiceIds('security.expressions.function_compiler') + as $id => $attr) { + $compilerDef->addMethodCall('addFunctionCompiler', array(new Reference($id))); + } + + foreach ($container->findTaggedServiceIds('security.expressions.type_compiler') + as $id => $attr) { + $compilerDef->addMethodCall('addTypeCompiler', array(new Reference($id))); + } + + $serviceMap = $parameterMap = array(); + foreach ($container->findTaggedServiceIds('security.expressions.variable') as $id => $attributes) { + foreach ($attributes as $attr) { + if (!isset($attr['variable']) || (!isset($attr['service']) && !isset($attr['parameter']))) { + throw new RuntimeException(sprintf('"variable", and either "service" or "parameter" must be given for tag "security.expressions.variable" for service id "%s".', $id)); + } + + if (isset($attr['service'])) { + $serviceMap[$attr['variable']] = $attr['service']; + $container + ->findDefinition($attr['service']) + ->setPublic(true) + ; + } else { + $parameterMap[$attr['variable']] = $attr['parameter']; + } + } + } + $container->getDefinition('security.expressions.variable_compiler') + ->addMethodCall('setMaps', array($serviceMap, $parameterMap)); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/CollectSecuredServicesPass.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/CollectSecuredServicesPass.php new file mode 100644 index 0000000..b813b89 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/CollectSecuredServicesPass.php @@ -0,0 +1,43 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Collects secured services. + * + * @author Johannes M. Schmitt + */ +class CollectSecuredServicesPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $securedClasses = array(); + foreach ($container->findTaggedServiceIds('security.secure_service') as $id => $attr) { + $securedClasses[] = $container->getDefinition($id)->getClass(); + } + + $container + ->getDefinition('security.access.pointcut') + ->addMethodCall('setSecuredClasses', array($securedClasses)) + ; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/DisableVotersPass.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/DisableVotersPass.php new file mode 100644 index 0000000..fb35bb9 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/DisableVotersPass.php @@ -0,0 +1,46 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +class DisableVotersPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if ($container->getParameter('security.role_voter.disabled')) { + $container->removeDefinition('security.access.role_hierarchy_voter'); + $container->removeDefinition('security.access.simple_role_voter'); + } + + if ($container->getParameter('security.authenticated_voter.disabled')) { + $container->removeDefinition('security.access.authenticated_voter'); + } + + if ($container->hasDefinition('security.acl.voter.basic_permissions')) { + if ($container->getParameter('security.acl_voter.disabled')) { + $container->removeDefinition('security.acl.voter.basic_permissions'); + } else { + $container->getDefinition('security.acl.voter.basic_permissions') + ->setClass('JMS\SecurityExtraBundle\Security\Acl\Voter\AclVoter'); + } + } + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/IntegrationPass.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/IntegrationPass.php new file mode 100644 index 0000000..abfcdb2 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Compiler/IntegrationPass.php @@ -0,0 +1,22 @@ +hasAlias('security.acl.provider') + && !$container->hasDefinition('security.acl.provider')) { + $container->removeDefinition('security.acl.permission_evaluator'); + } + + if ($container->hasDefinition('security.role_hierarchy')) { + $container->getDefinition('security.role_hierarchy') + ->setPublic(true); + } + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Configuration.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..70cf1d5 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/Configuration.php @@ -0,0 +1,64 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +class Configuration implements ConfigurationInterface +{ + public function getConfigTreeBuilder() + { + $tb = new TreeBuilder(); + $tb + ->root('jms_security_extra') + ->validate() + ->always(function($v) { + if ($v['method_access_control'] && !$v['expressions']) { + throw new \Exception('You need to enable expressions if you want to configure method access via the DI config.'); + } + + return $v; + }) + ->end() + ->children() + ->booleanNode('secure_all_services')->defaultFalse()->end() + ->booleanNode('enable_iddqd_attribute')->defaultFalse()->end() + ->scalarNode('cache_dir')->cannotBeEmpty()->defaultValue('%kernel.cache_dir%/jms_security')->end() + ->booleanNode('expressions')->defaultFalse()->end() + ->arrayNode('voters') + ->addDefaultsIfNotSet() + ->canBeUnset() + ->children() + ->booleanNode('disable_authenticated')->defaultFalse()->end() + ->booleanNode('disable_role')->defaultFalse()->end() + ->booleanNode('disable_acl')->defaultFalse()->end() + ->end() + ->end() + ->arrayNode('method_access_control') + ->useAttributeAsKey('pattern') + ->prototype('scalar')->isRequired()->cannotBeEmpty()->end() + ->end() + ->end() + ->end() + ; + + return $tb; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/JMSSecurityExtraExtension.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/JMSSecurityExtraExtension.php new file mode 100644 index 0000000..cd2e1c2 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/JMSSecurityExtraExtension.php @@ -0,0 +1,100 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\Reference; + +use JMS\SecurityExtraBundle\Exception\RuntimeException; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * JMSSecurityExtraExtension. + * + * @author Johannes M. Schmitt + */ +class JMSSecurityExtraExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $bundles = $container->getParameter('kernel.bundles'); + if (!isset($bundles['JMSAopBundle'])) { + throw new RuntimeException('The JMSSecurityExtraBundle requires the JMSAopBundle, please make sure to enable it in your AppKernel.'); + } + + $config = $this->processConfiguration(new Configuration(), $configs); + + $loader = new XmlFileLoader($container, new FileLocator(array(__DIR__.'/../Resources/config/'))); + $loader->load('services.xml'); + + $container->setParameter('security.access.secure_all_services', $config['secure_all_services']); + + $cacheDir = $container->getParameterBag()->resolveValue($config['cache_dir']); + if (!is_dir($cacheDir)) { + if (false === @mkdir($cacheDir, 0777, true)) { + throw new RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir)); + } + } + $container->setParameter('security.extra.cache_dir', $cacheDir); + + if ($config['expressions']) { + $loader->load('security_expressions.xml'); + + if (!is_dir($cacheDir.'/expressions')) { + if (false === @mkdir($cacheDir.'/expressions', 0777, true)) { + throw new RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir.'/expressions')); + } + } + + $container->getDefinition('security.expressions.voter') + ->addMethodCall('setCacheDir', array($cacheDir.'/expressions')); + } + + $disableAllVoters = !isset($config['voters']); + $container->setParameter('security.authenticated_voter.disabled', + $disableAllVoters || $config['voters']['disable_authenticated']); + $container->setParameter('security.role_voter.disabled', + $disableAllVoters || $config['voters']['disable_role']); + $container->setParameter('security.acl_voter.disabled', + $disableAllVoters || $config['voters']['disable_acl']); + + if ($config['enable_iddqd_attribute']) { + $container + ->getDefinition('security.extra.iddqd_voter') + ->addTag('security.voter') + ; + + // FIXME: Also add an iddqd after invocation provider + } + + if ($config['method_access_control']) { + $driverDef = $container->getDefinition('security.extra.driver_chain'); + $args = $driverDef->getArguments(); + array_unshift($args[0], new Reference('security.extra.config_driver')); + $driverDef->setArguments($args); + + $container->setParameter('security.access.method_access_control', + $config['method_access_control']); + } + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/SecurityExtension.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/SecurityExtension.php new file mode 100644 index 0000000..96ddf99 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/DependencyInjection/SecurityExtension.php @@ -0,0 +1,130 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\DependencyInjection; + +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension as BaseSecurityExtension; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Config\FileLocator; + +/** + * Enhances the access_control section of the SecurityBundle. + * + * @author Johannes M. Schmitt + */ +class SecurityExtension extends Extension +{ + private $extension; + + public function __construct(BaseSecurityExtension $extension) + { + $this->extension = $extension; + } + + public function getAlias() + { + return $this->extension->getAlias(); + } + + public function getNamespace() + { + return $this->extension->getNamespace(); + } + + public function getXsdValidationBasePath() + { + return $this->extension->getXsdValidationBasePath(); + } + + public function getClassesToCompile() + { + return array_merge(parent::getClassesToCompile(), $this->extension->getClassesToCompile()); + } + + public function load(array $configs, ContainerBuilder $container) + { + $parentConfigs = array(); + + foreach ($configs as $config) { + if (isset($config['rule'])) { + unset($config['rule']); + } + if (isset($config['access_control'])) { + unset($config['access_control']); + } + + $parentConfigs[] = $config; + } + $this->extension->load($parentConfigs, $container); + + $config = $this->processConfiguration(new AccessControlConfiguration(), $configs); + $this->createAuthorization($config, $container); + } + + public function __call($method, array $args) + { + return call_user_func_array(array($this->extension, $method), $args); + } + + private function createAuthorization($config, ContainerBuilder $container) + { + if (!$config['access_control']) { + return; + } + + $this->addClassesToCompile(array( + 'Symfony\\Component\\Security\\Http\\AccessMap', + )); + + foreach ($config['access_control'] as $access) { + $matcher = $this->invokeParent('createRequestMatcher', array( + $container, + $access['path'], + $access['host'], + count($access['methods']) === 0 ? null : $access['methods'], + $access['ip'] + )); + + if (isset($access['roles'])) { + $attributes = $access['roles']; + } else { + $def = new DefinitionDecorator('security.expressions.expression'); + $def->addArgument($access['access']); + $container->setDefinition($exprId = 'security.expressions.expression.'.sha1($access['access']), $def); + + $attributes = array(new Reference($exprId)); + } + + $container->getDefinition('security.access_map') + ->addMethodCall('add', array($matcher, $attributes, $access['requires_channel'])); + } + } + + private function invokeParent($method, array $args = array()) + { + $ref = new \ReflectionMethod($this->extension, $method); + $ref->setAccessible(true); + + return $ref->invokeArgs($this->extension, $args); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Exception/Exception.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Exception/Exception.php new file mode 100644 index 0000000..b6606f9 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Exception/Exception.php @@ -0,0 +1,28 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Exception; + +/** + * Base exception for the SecurityExtraBundle. + * + * @author Johannes M. Schmitt + */ +interface Exception +{ +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Exception/InvalidArgumentException.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..6a9f2ae --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Exception/InvalidArgumentException.php @@ -0,0 +1,28 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Exception; + +/** + * InvalidArgumentException for the SecurityExtraBundle. + * + * @author Johannes M. Schmitt + */ +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Exception/RuntimeException.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Exception/RuntimeException.php new file mode 100644 index 0000000..b7bb573 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Exception/RuntimeException.php @@ -0,0 +1,28 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Exception; + +/** + * RuntimeException for the SecurityExtraBundle. + * + * @author Johannes M. Schmitt + */ +class RuntimeException extends \RuntimeException implements Exception +{ +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/JMSSecurityExtraBundle.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/JMSSecurityExtraBundle.php new file mode 100644 index 0000000..6f5c0e3 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/JMSSecurityExtraBundle.php @@ -0,0 +1,59 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle; + +use JMS\SecurityExtraBundle\DependencyInjection\SecurityExtension; +use JMS\SecurityExtraBundle\DependencyInjection\Compiler\IntegrationPass; +use JMS\SecurityExtraBundle\DependencyInjection\Compiler\DisableVotersPass; +use JMS\SecurityExtraBundle\DependencyInjection\Compiler\AddExpressionCompilersPass; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use JMS\SecurityExtraBundle\DependencyInjection\Compiler\AddAfterInvocationProvidersPass; +use JMS\SecurityExtraBundle\DependencyInjection\Compiler\CollectSecuredServicesPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Registers our custom compiler pass. + * + * @author Johannes M. Schmitt + */ +class JMSSecurityExtraBundle extends Bundle +{ + const VERSION = '1.1.0'; + + public function build(ContainerBuilder $container) + { + if (!$container->hasExtension('security')) { + throw new \LogicException('The JMSSecurityExtraBundle must be registered after the SecurityBundle in your AppKernel.php.'); + } + $container->registerExtension(new SecurityExtension($container->getExtension('security'))); + + $passConfig = $container->getCompilerPassConfig(); + + // needs to run before voter collection + $passes = $passConfig->getBeforeOptimizationPasses(); + array_unshift($passes, new DisableVotersPass()); + $passConfig->setBeforeOptimizationPasses($passes); + + $passConfig->addPass(new AddAfterInvocationProvidersPass()); + $passConfig->addPass(new CollectSecuredServicesPass()); + $passConfig->addPass(new AddExpressionCompilersPass()); + $passConfig->addPass(new IntegrationPass()); + } +} diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Metadata/ClassMetadata.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Metadata/ClassMetadata.php new file mode 100644 index 0000000..5f692cb --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Metadata/ClassMetadata.php @@ -0,0 +1,93 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Metadata; + +use JMS\SecurityExtraBundle\Exception\RuntimeException; +use JMS\SecurityExtraBundle\Exception\InvalidArgumentException; +use Metadata\MethodMetadata; +use Metadata\MergeableInterface; +use Metadata\MergeableClassMetadata; + +/** + * Contains class metadata information + * + * @author Johannes M. Schmitt + */ +class ClassMetadata extends MergeableClassMetadata +{ + public function addMethodMetadata(MethodMetadata $metadata) + { + if ($this->reflection->isFinal()) { + throw new RuntimeException(sprintf('Class "%s" is declared final, and cannot be secured.', $reflection->name)); + } + + if ($metadata->reflection->isStatic()) { + throw new RuntimeException(sprintf('Method "%s::%s" is declared static and cannot be secured.', $metadata->reflection->class, $metadata->reflection->name)); + } + + if ($metadata->reflection->isFinal()) { + throw new RuntimeException(sprintf('Method "%s::%s" is declared final and cannot be secured.', $metadata->reflection->class, $metadata->reflection->name)); + } + + parent::addMethodMetadata($metadata); + } + + public function merge(MergeableInterface $metadata) + { + if (!$metadata instanceof ClassMetadata) { + throw new InvalidArgumentException('$metadata must be an instance of ClassMetadata.'); + } + + foreach ($this->methodMetadata as $name => $methodMetadata) { + // check if metadata was declared on an interface + if (!$metadata->reflection->hasMethod($name)) { + continue; + } + + if ($metadata->reflection->getMethod($name)->getDeclaringClass()->name + !== $methodMetadata->class) { + if (!isset($metadata->methodMetadata[$name])) { + if ($methodMetadata->reflection->isAbstract()) { + continue; + } + + throw new RuntimeException(sprintf( + 'You have overridden a secured method "%s::%s" in "%s". ' + .'Please copy over the applicable security metadata, and ' + .'also add @SatisfiesParentSecurityPolicy.', + $methodMetadata->reflection->class, + $name, + $metadata->reflection->name + )); + } + + if (!$metadata->methodMetadata[$name]->satisfiesParentSecurityPolicy) { + throw new RuntimeException(sprintf('Unresolved security metadata conflict for method "%s::%s" in "%s". Please copy the respective annotations, and add @SatisfiesParentSecurityPolicy to the child method.', $metadata->reflection->name, $name, $methodMetadata->reflection->getDeclaringClass()->getFilename())); + } + } + } + + parent::merge($metadata); + } + + public function isProxyRequired() + { + return !empty($this->methodMetadata); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Metadata/Driver/AnnotationDriver.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Metadata/Driver/AnnotationDriver.php new file mode 100644 index 0000000..7617c60 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Metadata/Driver/AnnotationDriver.php @@ -0,0 +1,107 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Metadata\Driver; + +use JMS\SecurityExtraBundle\Exception\InvalidArgumentException; +use Doctrine\Common\Annotations\Reader; +use JMS\SecurityExtraBundle\Annotation\PreAuthorize; +use JMS\SecurityExtraBundle\Annotation\RunAs; +use JMS\SecurityExtraBundle\Annotation\SatisfiesParentSecurityPolicy; +use JMS\SecurityExtraBundle\Annotation\Secure; +use JMS\SecurityExtraBundle\Annotation\SecureParam; +use JMS\SecurityExtraBundle\Annotation\SecureReturn; +use JMS\SecurityExtraBundle\Metadata\ClassMetadata; +use JMS\SecurityExtraBundle\Metadata\MethodMetadata; +use Metadata\Driver\DriverInterface; +use \ReflectionClass; +use \ReflectionMethod; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Expression; + +/** + * Loads security annotations and converts them to metadata + * + * @author Johannes M. Schmitt + */ +class AnnotationDriver implements DriverInterface +{ + private $reader; + + public function __construct(Reader $reader) + { + $this->reader = $reader; + } + + public function loadMetadataForClass(ReflectionClass $reflection) + { + $metadata = new ClassMetadata($reflection->getName()); + + foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED) as $method) { + // check if the method was defined on this class + if ($method->getDeclaringClass()->getName() !== $reflection->getName()) { + continue; + } + + $annotations = $this->reader->getMethodAnnotations($method); + + if ($annotations && null !== $methodMetadata = $this->convertMethodAnnotations($method, $annotations)) { + $metadata->addMethodMetadata($methodMetadata); + } + } + + return $metadata; + } + + private function convertMethodAnnotations(\ReflectionMethod $method, array $annotations) + { + $parameters = array(); + foreach ($method->getParameters() as $index => $parameter) { + $parameters[$parameter->getName()] = $index; + } + + $methodMetadata = new MethodMetadata($method->getDeclaringClass()->getName(), $method->getName()); + $hasSecurityMetadata = false; + foreach ($annotations as $annotation) { + if ($annotation instanceof Secure) { + $methodMetadata->roles = $annotation->roles; + $hasSecurityMetadata = true; + } else if ($annotation instanceof PreAuthorize) { + $methodMetadata->roles = array(new Expression($annotation->expr)); + $hasSecurityMetadata = true; + } else if ($annotation instanceof SecureParam) { + if (!isset($parameters[$annotation->name])) { + throw new InvalidArgumentException(sprintf('The parameter "%s" does not exist for method "%s".', $annotation->name, $method->getName())); + } + + $methodMetadata->addParamPermissions($parameters[$annotation->name], $annotation->permissions); + $hasSecurityMetadata = true; + } else if ($annotation instanceof SecureReturn) { + $methodMetadata->returnPermissions = $annotation->permissions; + $hasSecurityMetadata = true; + } else if ($annotation instanceof SatisfiesParentSecurityPolicy) { + $methodMetadata->satisfiesParentSecurityPolicy = true; + $hasSecurityMetadata = true; + } else if ($annotation instanceof RunAs) { + $methodMetadata->runAsRoles = $annotation->roles; + $hasSecurityMetadata = true; + } + } + + return $hasSecurityMetadata ? $methodMetadata : null; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Metadata/Driver/ConfigDriver.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Metadata/Driver/ConfigDriver.php new file mode 100644 index 0000000..00dd89e --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Metadata/Driver/ConfigDriver.php @@ -0,0 +1,100 @@ + + */ +class ConfigDriver implements DriverInterface +{ + private $bundles; + private $config; + + public function __construct(array $bundles, array $config) + { + uasort($bundles, function($a, $b) { + return strlen($b) - strlen($a); + }); + + foreach ($bundles as $name => $namespace) { + $bundles[$name] = substr($namespace, 0, strrpos($namespace, '\\')); + } + + $this->bundles = $bundles; + $this->config = $config; + } + + public function loadMetadataForClass(\ReflectionClass $class) + { + $metadata = new ClassMetadata($class->name); + + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) { + if ($method->getDeclaringClass()->name !== $class->name) { + continue; + } + + $expression = null; + if (null !== $notation = $this->getControllerNotation($method)) { + $expression = $this->getExpressionForSignature($notation); + } + + if (null === $expression && null === $expression = + $this->getExpressionForSignature($method->class.'::'.$method->name)) { + continue; + } + + $methodMetadata = new MethodMetadata($method->class, $method->name); + $methodMetadata->roles = array(new Expression($expression)); + $metadata->addMethodMetadata($methodMetadata); + } + + if (!$metadata->methodMetadata) { + return null; + } + + return $metadata; + } + + private function getExpressionForSignature($signature) + { + foreach ($this->config as $pattern => $expr) { + if (!preg_match('#'.$pattern.'#i', $signature)) { + continue; + } + + return $expr; + } + + return null; + } + + // TODO: Is it feasible to reverse-engineer the notation for service controllers? + private function getControllerNotation(\ReflectionMethod $method) + { + $signature = $method->class.'::'.$method->name; + + // check if class is a controller + if (0 === preg_match('#\\\\Controller\\\\([^\\\\]+)Controller::(.+)Action$#', $signature, $match)) { + return null; + } + + foreach ($this->bundles as $name => $namespace) { + if (0 !== strpos($method->class, $namespace)) { + continue; + } + + // controller notation (AcmeBundle:Foo:foo) + return $name.':'.$match[1].':'.$match[2]; + } + + return null; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Metadata/MethodMetadata.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Metadata/MethodMetadata.php new file mode 100644 index 0000000..6bec7ce --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Metadata/MethodMetadata.php @@ -0,0 +1,84 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Metadata; + +use Metadata\MethodMetadata as BaseMethodMetadata; + +/** + * Contains method metadata information + * + * @author Johannes M. Schmitt + */ +class MethodMetadata extends BaseMethodMetadata +{ + public $roles = array(); + public $paramPermissions = array(); + public $returnPermissions = array(); + public $runAsRoles = array(); + public $satisfiesParentSecurityPolicy = false; + + /** + * Adds a parameter restriction + * + * @param integer $index 0-based + * @param array $permissions + */ + public function addParamPermissions($index, array $permissions) + { + $this->paramPermissions[$index] = $permissions; + } + + public function isDeclaredOnInterface() + { + foreach ($this->reflection->getDeclaringClass()->getInterfaces() as $interface) { + if ($interface->hasMethod($this->name)) { + return true; + } + } + + return false; + } + + /** + * This allows to merge in metadata from an interface + * + * @param MethodMetadata $method + * @return void + */ + public function merge(MethodMetadata $method) + { + if (!$this->roles) { + $this->roles = $method->roles; + } + + if (!$this->returnPermissions) { + $this->returnPermissions = $method->returnPermissions; + } + + if (!$this->runAsRoles) { + $this->runAsRoles = $method->runAsRoles; + } + + foreach ($method->paramPermissions as $index => $permissions) { + if (!isset($this->paramPermissions[$index])) { + $this->paramPermissions[$index] = $permissions; + } + } + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/README b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/README new file mode 100644 index 0000000..72bab21 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/README @@ -0,0 +1,8 @@ +For documentation, see: + + Resources/doc + + +For license, see: + + Resources/meta/LICENSE \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Resources/config/security_expressions.xml b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Resources/config/security_expressions.xml new file mode 100644 index 0000000..493e2ea --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Resources/config/security_expressions.xml @@ -0,0 +1,84 @@ + + + + + + JMS\SecurityExtraBundle\Security\Acl\Expression\PermissionEvaluator + JMS\SecurityExtraBundle\Security\Acl\Expression\HasPermissionFunctionCompiler + + JMS\SecurityExtraBundle\Security\Authorization\Expression\LazyLoadingExpressionVoter + JMS\SecurityExtraBundle\Security\Authorization\Expression\ContainerAwareExpressionHandler + + JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler + JMS\SecurityExtraBundle\Security\Authorization\Expression\Expression + + JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\ContainerAwareVariableCompiler + JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\ParameterExpressionCompiler + + JMS\SecurityExtraBundle\Metadata\Driver\ConfigDriver + + JMS\SecurityExtraBundle\Twig\SecurityExtension + + + + + + + + + + %kernel.bundles% + %security.access.method_access_control% + + + + + + + + + + + + + + + + + + + + security.expressions.compiler + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Resources/config/services.xml b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Resources/config/services.xml new file mode 100644 index 0000000..0ad446c --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Resources/config/services.xml @@ -0,0 +1,99 @@ + + + + + + + + JMS\SecurityExtraBundle\Security\Authorization\Interception\MethodSecurityInterceptor + + + JMS\SecurityExtraBundle\Security\Authorization\RunAsManager + JMS\SecurityExtraBundle\Security\Authentication\Provider\RunAsAuthenticationProvider + RunAsToken + ROLE_ + + JMS\SecurityExtraBundle\Security\Authorization\AfterInvocation\AfterInvocationManager + JMS\SecurityExtraBundle\Security\Authorization\AfterInvocation\AclAfterInvocationProvider + + JMS\SecurityExtraBundle\Security\Authorization\Voter\IddqdVoter + + Metadata\MetadataFactory + Metadata\Driver\LazyLoadingDriver + Metadata\Driver\DriverChain + JMS\SecurityExtraBundle\Metadata\Driver\AnnotationDriver + Metadata\Cache\FileCache + + + + + %security.run_as.key% + %security.run_as.role_prefix% + + + + + + + + + + + + + + + %security.access.secure_all_services% + %security.access.method_access_control% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + security.extra.metadata_driver + + + + + + + + %security.extra.cache_dir% + %kernel.debug% + + + + + + \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Resources/doc/index.rst b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Resources/doc/index.rst new file mode 100644 index 0000000..8b41a2f --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Resources/doc/index.rst @@ -0,0 +1,352 @@ +======== +Overview +======== + +This bundle enhances the Symfony2 Security Component by adding several new features. + +Features: + +- powerful expression-based authorization language +- method security authorization +- authorization configuration via annotations + +Installation +------------ +Add the following to your ``deps`` file:: + + [JMSSecurityExtraBundle] + git=https://github.com/schmittjoh/JMSSecurityExtraBundle.git + target=/bundles/JMS/SecurityExtraBundle + + ; Dependencies: + ;-------------- + [metadata] + git=https://github.com/schmittjoh/metadata.git + version=1.1.0 ; <- make sure to get 1.1, not 1.0 + + ; see https://github.com/schmittjoh/JMSAopBundle/blob/master/Resources/doc/index.rst + [JMSAopBundle] + git=https://github.com/schmittjoh/JMSAopBundle.git + target=/bundles/JMS/AopBundle + + [cg-library] + git=https://github.com/schmittjoh/cg-library.git + + ; This dependency is optional (you need it if you are using non-service controllers): + ; see https://github.com/schmittjoh/JMSDiExtraBundle/blob/master/Resources/doc/index.rst + [JMSDiExtraBundle] + git=https://github.com/schmittjoh/JMSDiExtraBundle.git + target=/bundles/JMS/DiExtraBundle + +Then register the bundle with your kernel:: + + // in AppKernel::registerBundles() + $bundles = array( + // ... + new JMS\AopBundle\JMSAopBundle(), + new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(), + new JMS\DiExtraBundle\JMSDiExtraBundle($this), + // ... + ); + +Make sure that you also register the namespaces with the autoloader:: + + // app/autoload.php + $loader->registerNamespaces(array( + // ... + 'JMS' => __DIR__.'/../vendor/bundles', + 'Metadata' => __DIR__.'/../vendor/metadata/src', + 'CG' => __DIR__.'/../vendor/cg-library/src', + // ... + )); + +Configuration +------------- + +Below, you find the default configuration:: + + # app/config/config.yml + jms_security_extra: + # Whether you want to secure all services (true), or only secure specific + # services (false); see also below + secure_all_services: false + + # Enabling this setting will add an additional special attribute "IS_IDDQD". + # Anybody with this attribute will effectively bypass all security checks. + enable_iddqd_attribute: false + + # Enables expression language + expressions: false + + # Allows you to disable some, or all built-in voters + voters: + disable_authenticated: false + disable_role: false + disable_acl: false + + # Allows you to specify access control rules for specific methods, such + # as controller actions + method_access_control: { } + + +Expression-based Authorization Language +--------------------------------------- +The expression language is a very powerful alternative to the simple attributes +of the security voting system. They allow to perform complex access decision +checks, and because they are compiled down to raw PHP, they are much faster than +the built-in voters. Also they are lazy-loading by nature, so you will also +save some resources for example by not having to initialize the entire ACL system +on each request. + +Programmatic Usage +~~~~~~~~~~~~~~~~~~ +You can execute expressions programmatically by using the ``isGranted`` method +of the SecurityContext. Some examples:: + + use JMS\SecurityExtraBundle\Security\Authorization\Expression\Expression; + + $securityContext->isGranted(array(new Expression('hasRole("A")'))); + $securityContext->isGranted(array(new Expression('hasRole("A") or (hasRole("B") and hasRole("C"))'))); + $securityContext->isGranted(array(new Expression('hasPermission(object, "VIEW")'), $object)); + $securityContext->isGranted(array(new Expression('token.getUsername() == "Johannes"'))); + +Twig Usage +~~~~~~~~~~ +You can check expressions from Twig templates using the ``is_expr_granted`` +function. Some examples:: + + is_expr_granted("hasRole('FOO')") + is_expr_granted("hasPermission(object, 'VIEW')", object) + +Usage in Access Control +~~~~~~~~~~~~~~~~~~~~~~~ +You can also use expressions in the ``access_control``:: + + security: + access_control: + - { path: ^/foo, access: "hasRole('FOO') and hasRole('BAR')" } + +Annotation-based Usage +~~~~~~~~~~~~~~~~~~~~~~ +see @PreAuthorize in the annotation reference + +Reference +~~~~~~~~~ ++-----------------------------------+--------------------------------------------+ +| Expression | Description | ++===================================+============================================+ +| hasRole('ROLE') | Checks whether the token has a certain | +| | role. | ++-----------------------------------+--------------------------------------------+ +| hasAnyRole('ROLE1', 'ROLE2', ...) | Checks whether the token has any of the | +| | given roles. | ++-----------------------------------+--------------------------------------------+ +| isAnonymous() | Checks whether the token is anonymous. | ++-----------------------------------+--------------------------------------------+ +| isRememberMe() | Checks whether the token is remember me. | ++-----------------------------------+--------------------------------------------+ +| isFullyAuthenticated() | Checks whether the token is fully | +| | authenticated. | ++-----------------------------------+--------------------------------------------+ +| isAuthenticated() | Checks whether the token is not anonymous. | ++-----------------------------------+--------------------------------------------+ +| hasPermission(*var*, 'PERMISSION')| Checks whether the token has the given | +| | permission for the given object (requires | +| | the ACL system). | ++-----------------------------------+--------------------------------------------+ +| token | Variable that refers to the token | +| | which is currently in the security context.| ++-----------------------------------+--------------------------------------------+ +| user | Variable that refers to the user | +| | which is currently in the security context.| ++-----------------------------------+--------------------------------------------+ +| object | Variable that refers to the object for | +| | which access is being requested. | ++-----------------------------------+--------------------------------------------+ +| #*paramName* | Any identifier prefixed with # refers to | +| | a parameter of the same name that is passed| +| | to the method where the expression is used.| ++-----------------------------------+--------------------------------------------+ +| and / && | Binary "and" operator | ++-----------------------------------+--------------------------------------------+ +| or / || | Binary "or" operator | ++-----------------------------------+--------------------------------------------+ +| == | Binary "is equal" operator | ++-----------------------------------+--------------------------------------------+ +| not / ! | Negation operator | ++-----------------------------------+--------------------------------------------+ + +Method Security Authorization +----------------------------- +Generally, you can secure all public, or protected methods which are non-static, +and non-final. Private methods cannot be secured. You can also add metadata for +abstract methods, or interfaces which will then be applied to their concrete +implementations automatically. + +Access Control via DI configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +You can specify access control **expressions** in the DI configuration:: + + # config.yml + jms_security_extra: + method_access_control: + ':loginAction$': 'isAnonymous()' + 'AcmeFooBundle:.*:deleteAction': 'hasRole("ROLE_ADMIN")' + '^MyNamespace\MyService::foo$': 'hasPermission(#user, "VIEW")' + +The pattern is a case-sensitive regular expression which is matched against two notations. +The first match is being used. + +First, your pattern is matched against the notation for non-service controllers. +This obviously is only done if your class is actually a controller, e.g. +``AcmeFooBundle:Add:new`` for a controller named ``AddController`` and a method +named ``newAction`` in a sub-namespace ``Controller`` in a bundle named ``AcmeFooBundle``. + +Last, your pattern is matched against the concatenation of the class name, and +the method name that is being called, e.g. ``My\Fully\Qualified\ClassName::myMethodName``. + +**Note:** If you would like to secure non-service controllers, the +``JMSDiExtraBundle`` must be installed. + +Access Control via Annotations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If you like to secure a service with annotations, you need to enable annotation +configuration for this service:: + + + + + +In case, you like to configure all services via annotations, you can also set +``secure_all_services`` to true. Then, you do not need to add a tag for each +service. + + +Annotations +----------- +@PreAuthorize +~~~~~~~~~~~~~ +This annotation lets you define an expression (see the expression language +paragraph) which is executed prior to invoking a method:: + + myPrivateService->aMethodOnlyToBeInvokedThroughASpecificChannel(); + } + } + +@SatisfiesParentSecurityPolicy +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This must be defined on a method that overrides a method which has security metadata. +It is there to ensure that you are aware the security of the overridden method cannot +be enforced anymore, and that you must copy over all annotations if you want to keep +them. diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Resources/meta/LICENSE b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Resources/meta/LICENSE new file mode 100644 index 0000000..753842b --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Resources/meta/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Acl/Expression/HasPermissionFunctionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Acl/Expression/HasPermissionFunctionCompiler.php new file mode 100644 index 0000000..340617b --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Acl/Expression/HasPermissionFunctionCompiler.php @@ -0,0 +1,63 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Acl\Expression; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ConstantExpression; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func\FunctionCompilerInterface; + +class HasPermissionFunctionCompiler implements FunctionCompilerInterface +{ + public function getName() + { + return 'hasPermission'; + } + + public function compilePreconditions(ExpressionCompiler $compiler, FunctionExpression $function) + { + $compiler->verifyItem('token', 'Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + } + + public function compile(ExpressionCompiler $compiler, FunctionExpression $function) + { + $compiler + ->compileInternal(new VariableExpression('permission_evaluator')) + ->write('->hasPermission(') + ->compileInternal(new VariableExpression('token')) + ->write(', ') + ->compileInternal($function->args[0]) + ->write(', ') + ; + + if ($function->args[1] instanceof ConstantExpression) { + $compiler->write(var_export(strtoupper($function->args[1]->value), true).')'); + + return; + } + + $compiler + ->write('strtoupper(') + ->compileInternal($function->args[1]) + ->write('))') + ; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Acl/Expression/PermissionEvaluator.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Acl/Expression/PermissionEvaluator.php new file mode 100644 index 0000000..96111fc --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Acl/Expression/PermissionEvaluator.php @@ -0,0 +1,122 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Acl\Expression; + +use Symfony\Component\Security\Acl\Exception\NoAceFoundException; +use Symfony\Component\Security\Acl\Exception\AclNotFoundException; +use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface; +use Symfony\Component\Security\Acl\Permission\PermissionMapInterface; +use Symfony\Component\Security\Acl\Model\AclProviderInterface; +use Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface; +use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface; +use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +class PermissionEvaluator +{ + private $aclProvider; + private $oidRetrievalStrategy; + private $sidRetrievalStrategy; + private $permissionMap; + private $allowIfObjectIdentityUnavailable; + private $logger; + + public function __construct(AclProviderInterface $aclProvider, + ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy, + SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy, + PermissionMapInterface $permissionMap, + $allowIfObjectIdentityUnavailable = true, + LoggerInterface $logger = null) + { + $this->aclProvider = $aclProvider; + $this->oidRetrievalStrategy = $oidRetrievalStrategy; + $this->sidRetrievalStrategy = $sidRetrievalStrategy; + $this->permissionMap = $permissionMap; + $this->allowIfObjectIdentityUnavailable = $allowIfObjectIdentityUnavailable; + $this->logger = $logger; + } + + public function hasPermission(TokenInterface $token, $object, $permission) + { + if (null === $masks = $this->permissionMap->getMasks($permission, $object)) { + return false; + } + + if (null === $object) { + if (null !== $this->logger) { + $this->logger->debug(sprintf('Object identity unavailable. Voting to %s', $this->allowIfObjectIdentityUnavailable? 'grant access' : 'abstain')); + } + + return $this->allowIfObjectIdentityUnavailable ? true : false; + } else if ($object instanceof FieldVote) { + $field = $object->getField(); + $object = $object->getDomainObject(); + } else { + $field = null; + } + + if ($object instanceof ObjectIdentityInterface) { + $oid = $object; + } else if (null === $oid = $this->oidRetrievalStrategy->getObjectIdentity($object)) { + if (null !== $this->logger) { + $this->logger->debug(sprintf('Object identity unavailable. Voting to %s', $this->allowIfObjectIdentityUnavailable? 'grant access' : 'abstain')); + } + + return $this->allowIfObjectIdentityUnavailable ? true : false; + } + + $sids = $this->sidRetrievalStrategy->getSecurityIdentities($token); + + try { + $acl = $this->aclProvider->findAcl($oid, $sids); + + if (null === $field && $acl->isGranted($masks, $sids, false)) { + if (null !== $this->logger) { + $this->logger->debug('ACL found, permission granted. Voting to grant access'); + } + + return true; + } else if (null !== $field && $acl->isFieldGranted($field, $masks, $sids, false)) { + if (null !== $this->logger) { + $this->logger->debug('ACL found, permission granted. Voting to grant access'); + } + + return true; + } + + if (null !== $this->logger) { + $this->logger->debug('ACL found, insufficient permissions. Voting to deny access.'); + } + + return false; + } catch (AclNotFoundException $noAcl) { + if (null !== $this->logger) { + $this->logger->debug('No ACL found for the object identity. Voting to deny access.'); + } + + return false; + } catch (NoAceFoundException $noAce) { + if (null !== $this->logger) { + $this->logger->debug('ACL found, no ACE applicable. Voting to deny access.'); + } + + return false; + } + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Acl/Voter/AclVoter.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Acl/Voter/AclVoter.php new file mode 100644 index 0000000..bad27d8 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Acl/Voter/AclVoter.php @@ -0,0 +1,136 @@ + + */ +class AclVoter implements VoterInterface +{ + private $aclProvider; + private $permissionMap; + private $objectIdentityRetrievalStrategy; + private $securityIdentityRetrievalStrategy; + private $allowIfObjectIdentityUnavailable; + private $logger; + + public function __construct(AclProviderInterface $aclProvider, ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy, SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy, PermissionMapInterface $permissionMap, LoggerInterface $logger = null, $allowIfObjectIdentityUnavailable = true) + { + $this->aclProvider = $aclProvider; + $this->permissionMap = $permissionMap; + $this->objectIdentityRetrievalStrategy = $oidRetrievalStrategy; + $this->securityIdentityRetrievalStrategy = $sidRetrievalStrategy; + $this->logger = $logger; + $this->allowIfObjectIdentityUnavailable = $allowIfObjectIdentityUnavailable; + } + + public function supportsAttribute($attribute) + { + return $this->permissionMap->contains($attribute); + } + + public function vote(TokenInterface $token, $object, array $attributes) + { + foreach ($attributes as $attribute) { + if (null === $masks = $this->permissionMap->getMasks((string) $attribute, $object)) { + continue; + } + + if (null === $object) { + if (null !== $this->logger) { + $this->logger->debug(sprintf('Object identity unavailable. Voting to %s', $this->allowIfObjectIdentityUnavailable? 'grant access' : 'abstain')); + } + + return $this->allowIfObjectIdentityUnavailable ? self::ACCESS_GRANTED : self::ACCESS_ABSTAIN; + } else if ($object instanceof FieldVote) { + $field = $object->getField(); + $object = $object->getDomainObject(); + } else { + $field = null; + } + + if ($object instanceof ObjectIdentityInterface) { + $oid = $object; + } else if (null === $oid = $this->objectIdentityRetrievalStrategy->getObjectIdentity($object)) { + if (null !== $this->logger) { + $this->logger->debug(sprintf('Object identity unavailable. Voting to %s', $this->allowIfObjectIdentityUnavailable? 'grant access' : 'abstain')); + } + + return $this->allowIfObjectIdentityUnavailable ? self::ACCESS_GRANTED : self::ACCESS_ABSTAIN; + } + + if (!$this->supportsClass($oid->getType())) { + return self::ACCESS_ABSTAIN; + } + + $sids = $this->securityIdentityRetrievalStrategy->getSecurityIdentities($token); + + try { + $acl = $this->aclProvider->findAcl($oid, $sids); + + if (null === $field && $acl->isGranted($masks, $sids, false)) { + if (null !== $this->logger) { + $this->logger->debug('ACL found, permission granted. Voting to grant access'); + } + + return self::ACCESS_GRANTED; + } else if (null !== $field && $acl->isFieldGranted($field, $masks, $sids, false)) { + if (null !== $this->logger) { + $this->logger->debug('ACL found, permission granted. Voting to grant access'); + } + + return self::ACCESS_GRANTED; + } + + if (null !== $this->logger) { + $this->logger->debug('ACL found, insufficient permissions. Voting to deny access.'); + } + + return self::ACCESS_DENIED; + } catch (AclNotFoundException $noAcl) { + if (null !== $this->logger) { + $this->logger->debug('No ACL found for the object identity. Voting to deny access.'); + } + + return self::ACCESS_DENIED; + } catch (NoAceFoundException $noAce) { + if (null !== $this->logger) { + $this->logger->debug('ACL found, no ACE applicable. Voting to deny access.'); + } + + return self::ACCESS_DENIED; + } + } + + // no attribute was supported + return self::ACCESS_ABSTAIN; + } + + /** + * You can override this method when writing a voter for a specific domain + * class. + * + * @param string $class The class name + * + * @return Boolean + */ + public function supportsClass($class) + { + return true; + } +} diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authentication/Provider/RunAsAuthenticationProvider.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authentication/Provider/RunAsAuthenticationProvider.php new file mode 100644 index 0000000..6958805 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authentication/Provider/RunAsAuthenticationProvider.php @@ -0,0 +1,57 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authentication\Provider; + +use Symfony\Component\Security\Core\Exception\BadCredentialsException; +use JMS\SecurityExtraBundle\Security\Authentication\Token\RunAsUserToken; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; + +/** + * Class which authenticates RunAsTokens. + * + * @author Johannes M. Schmitt + */ +class RunAsAuthenticationProvider implements AuthenticationProviderInterface +{ + private $key; + + public function __construct($key) + { + $this->key = $key; + } + + public function authenticate(TokenInterface $token) + { + if (!$this->supports($token)) { + return null; + } + + if ($token->getKey() === $this->key) { + return $token; + } else { + throw new BadCredentialsException('The keys do not match.'); + } + } + + public function supports(TokenInterface $token) + { + return $token instanceof RunAsUserToken; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authentication/Token/RunAsUserToken.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authentication/Token/RunAsUserToken.php new file mode 100644 index 0000000..0672f2a --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authentication/Token/RunAsUserToken.php @@ -0,0 +1,85 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authentication\Token; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; + +/** + * This token is automatically generated by the RunAsManager when an invocation + * is supposed to be run with a different Token. + * + * @author Johannes M. Schmitt + */ +class RunAsUserToken extends AbstractToken +{ + private $originalToken; + private $key; + private $credentials; + + public function __construct($key, $user, $credentials, array $roles, TokenInterface $originalToken) + { + parent::__construct($roles); + + $this->originalToken = $originalToken; + $this->credentials = $credentials; + $this->key = $key; + + $this->setUser($user); + $this->setAuthenticated(true); + } + + public function getKey() + { + return $this->key; + } + + public function getOriginalToken() + { + return $this->originalToken; + } + + public function getCredentials() + { + return $this->credentials; + } + + public function eraseCredentials() + { + parent::eraseCredentials(); + + $this->credentials = null; + } + + public function serialize() + { + return serialize(array( + $this->originalToken, + $this->key, + $this->credentials, + parent::serialize(), + )); + } + + public function unserialize($str) + { + list($this->originalToken, $this->key, $this->credentials, $parentStr) = unserialize($str); + parent::unserialize($parentStr); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/AfterInvocation/AclAfterInvocationProvider.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/AfterInvocation/AclAfterInvocationProvider.php new file mode 100644 index 0000000..a004e92 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/AfterInvocation/AclAfterInvocationProvider.php @@ -0,0 +1,111 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\AfterInvocation; + +use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Symfony\Component\Security\Acl\Exception\AclNotFoundException; +use Symfony\Component\Security\Acl\Exception\NoAceFoundException; +use Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface; +use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface; +use Symfony\Component\Security\Acl\Model\AclProviderInterface; +use Symfony\Component\Security\Acl\Permission\PermissionMapInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; + +/** + * This after invocation provider filters returned objects based on ACLs. + * + * @author Johannes M. Schmitt + */ +class AclAfterInvocationProvider implements AfterInvocationProviderInterface +{ + private $aclProvider; + private $oidRetrievalStrategy; + private $sidRetrievalStrategy; + private $permissionMap; + private $logger; + + public function __construct(AclProviderInterface $aclProvider, ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy, SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy, PermissionMapInterface $permissionMap, LoggerInterface $logger = null) + { + $this->aclProvider = $aclProvider; + $this->oidRetrievalStrategy = $oidRetrievalStrategy; + $this->sidRetrievalStrategy = $sidRetrievalStrategy; + $this->permissionMap = $permissionMap; + $this->logger = $logger; + } + + public function decide(TokenInterface $token, $secureObject, array $attributes, $returnedObject) + { + if (null === $returnedObject) { + if (null !== $this->logger) { + $this->logger->debug('Returned object was null, skipping security check.'); + } + + return null; + } + + foreach ($attributes as $attribute) { + if (!$this->supportsAttribute($attribute)) { + continue; + } + + if (null === $oid = $this->oidRetrievalStrategy->getObjectIdentity($returnedObject)) { + if (null !== $this->logger) { + $this->logger->debug('Returned object was no domain object, skipping security check.'); + } + + return $returnedObject; + } + + $sids = $this->sidRetrievalStrategy->getSecurityIdentities($token); + + try { + $acl = $this->aclProvider->findAcl($oid, $sids); + if ($acl->isGranted($this->permissionMap->getMasks($attribute, $returnedObject), $sids, false)) { + return $returnedObject; + } + + if (null !== $this->logger) { + $this->logger->debug('Token has been denied access for returned object.'); + } + } catch (AclNotFoundException $noAcl) { + throw new AccessDeniedException('No applicable ACL found for domain object.'); + } catch (NoAceFoundException $noAce) { + if (null !== $this->logger) { + $this->logger->debug('No applicable ACE found for the given Token, denying access.'); + } + } + + throw new AccessDeniedException('ACL has denied access for attribute: '.$attribute); + } + + // no attribute was supported + return $returnedObject; + } + + public function supportsAttribute($attribute) + { + return $this->permissionMap->contains($attribute); + } + + public function supportsClass($className) + { + return true; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/AfterInvocation/AfterInvocationManager.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/AfterInvocation/AfterInvocationManager.php new file mode 100644 index 0000000..88beeae --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/AfterInvocation/AfterInvocationManager.php @@ -0,0 +1,77 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\AfterInvocation; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * This is the pendant to the AccessDecisionManager which is used to make + * access decisions after a method has been executed. + * + * @author Johannes M. Schmitt + */ +class AfterInvocationManager implements AfterInvocationManagerInterface +{ + private $providers; + + public function __construct(array $providers) + { + $this->providers = $providers; + } + + /** + * {@inheritDoc} + */ + public function decide(TokenInterface $token, $secureInvocation, array $attributes, $returnedObject) + { + foreach ($this->providers as $provider) { + $returnedObject = $provider->decide($token, $secureInvocation, $attributes, $returnedObject); + } + + return $returnedObject; + } + + /** + * {@inheritDoc} + */ + public function supportsAttribute($attribute) + { + foreach ($this->providers as $provider) { + if (true === $provider->supportsAttribute($attribute)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function supportsClass($className) + { + foreach ($this->providers as $provider) { + if (true === $provider->supportsClass($className)) { + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/AfterInvocation/AfterInvocationManagerInterface.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/AfterInvocation/AfterInvocationManagerInterface.php new file mode 100644 index 0000000..c02ec50 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/AfterInvocation/AfterInvocationManagerInterface.php @@ -0,0 +1,56 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\AfterInvocation; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * AfterInvocationManagerInterface + * + * @author Johannes M. Schmitt + */ +interface AfterInvocationManagerInterface +{ + /** + * Makes an access decision after the invocation of a method + * + * @param TokenInterface $token + * @param object $secureObject + * @param array $attributes + * @param mixed $returnedValue the value that was returned by the method invocation + * @return mixed the filter return value + */ + function decide(TokenInterface $token, $secureObject, array $attributes, $returnedValue); + + /** + * Determines whether the given attribute is supported + * + * @param string $attribute + * @return Boolean + */ + function supportsAttribute($attribute); + + /** + * Determines whether the given class is supported + * + * @param string $className the class of the secure object + * @return Boolean + */ + function supportsClass($className); +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/AfterInvocation/AfterInvocationProviderInterface.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/AfterInvocation/AfterInvocationProviderInterface.php new file mode 100644 index 0000000..e75466b --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/AfterInvocation/AfterInvocationProviderInterface.php @@ -0,0 +1,33 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\AfterInvocation; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * AfterInvocationProviderInterface + * + * @author Johannes M. Schmitt + */ +interface AfterInvocationProviderInterface +{ + function decide(TokenInterface $token, $secureObject, array $attributes, $returnedObject); + function supportsAttribute($attribute); + function supportsClass($className); +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/AndExpression.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/AndExpression.php new file mode 100644 index 0000000..e01d396 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/AndExpression.php @@ -0,0 +1,31 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast; + +class AndExpression implements ExpressionInterface +{ + public $left; + public $right; + + public function __construct(ExpressionInterface $left, ExpressionInterface $right) + { + $this->left = $left; + $this->right = $right; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/ArrayExpression.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/ArrayExpression.php new file mode 100644 index 0000000..cb039bf --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/ArrayExpression.php @@ -0,0 +1,29 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast; + +class ArrayExpression implements ExpressionInterface +{ + public $elements; + + public function __construct(array $elements) + { + $this->elements = $elements; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/ConstantExpression.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/ConstantExpression.php new file mode 100644 index 0000000..963f1ed --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/ConstantExpression.php @@ -0,0 +1,29 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast; + +class ConstantExpression implements ExpressionInterface +{ + public $value; + + public function __construct($value) + { + $this->value = $value; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/ExpressionInterface.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/ExpressionInterface.php new file mode 100644 index 0000000..91af129 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/ExpressionInterface.php @@ -0,0 +1,23 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast; + +interface ExpressionInterface +{ +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/FunctionExpression.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/FunctionExpression.php new file mode 100644 index 0000000..5e52e98 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/FunctionExpression.php @@ -0,0 +1,32 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast; + +class FunctionExpression implements ExpressionInterface +{ + /** READ-ONLY */ + public $name; + public $args; + + public function __construct($name, array $args) + { + $this->name = $name; + $this->args = $args; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/GetItemExpression.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/GetItemExpression.php new file mode 100644 index 0000000..2df5eb0 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/GetItemExpression.php @@ -0,0 +1,31 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast; + +class GetItemExpression +{ + public $array; + public $key; + + public function __construct(ExpressionInterface $array, ExpressionInterface $key) + { + $this->array = $array; + $this->key = $key; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/GetPropertyExpression.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/GetPropertyExpression.php new file mode 100644 index 0000000..fe070ab --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/GetPropertyExpression.php @@ -0,0 +1,31 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast; + +class GetPropertyExpression implements ExpressionInterface +{ + public $object; + public $name; + + public function __construct(ExpressionInterface $obj, $name) + { + $this->object = $obj; + $this->name = $name; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/IsEqualExpression.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/IsEqualExpression.php new file mode 100644 index 0000000..89f83f7 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/IsEqualExpression.php @@ -0,0 +1,31 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast; + +class IsEqualExpression implements ExpressionInterface +{ + public $left; + public $right; + + public function __construct(ExpressionInterface $left, ExpressionInterface $right) + { + $this->left = $left; + $this->right = $right; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/MethodCallExpression.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/MethodCallExpression.php new file mode 100644 index 0000000..22f3274 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/MethodCallExpression.php @@ -0,0 +1,33 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast; + +class MethodCallExpression implements ExpressionInterface +{ + public $object; + public $method; + public $args; + + public function __construct(ExpressionInterface $obj, $method, array $args) + { + $this->object = $obj; + $this->method = $method; + $this->args = $args; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/NotExpression.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/NotExpression.php new file mode 100644 index 0000000..aa730c0 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/NotExpression.php @@ -0,0 +1,29 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast; + +class NotExpression implements ExpressionInterface +{ + public $expr; + + public function __construct(ExpressionInterface $expr) + { + $this->expr = $expr; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/OrExpression.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/OrExpression.php new file mode 100644 index 0000000..a3d1f03 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/OrExpression.php @@ -0,0 +1,31 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast; + +class OrExpression implements ExpressionInterface +{ + public $left; + public $right; + + public function __construct(ExpressionInterface $left, ExpressionInterface $right) + { + $this->left = $left; + $this->right = $right; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/ParameterExpression.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/ParameterExpression.php new file mode 100644 index 0000000..f382e5c --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/ParameterExpression.php @@ -0,0 +1,29 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast; + +class ParameterExpression implements ExpressionInterface +{ + public $name; + + public function __construct($name) + { + $this->name = $name; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/VariableExpression.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/VariableExpression.php new file mode 100644 index 0000000..c48c02e --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Ast/VariableExpression.php @@ -0,0 +1,31 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast; + +class VariableExpression implements ExpressionInterface +{ + public $name; + public $allowNull; + + public function __construct($name, $allowNull = false) + { + $this->name = $name; + $this->allowNull = $allowNull; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/AndExpressionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/AndExpressionCompiler.php new file mode 100644 index 0000000..e488a32 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/AndExpressionCompiler.php @@ -0,0 +1,32 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler; + +class AndExpressionCompiler extends BinaryExprCompiler +{ + public function getType() + { + return 'JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\AndExpression'; + } + + protected function getOperator() + { + return '&&'; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/BinaryExprCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/BinaryExprCompiler.php new file mode 100644 index 0000000..0dcd154 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/BinaryExprCompiler.php @@ -0,0 +1,51 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; + +/** + * Base Compiler for Binary Operators. + * + * @author Johannes M. Schmitt + */ +abstract class BinaryExprCompiler implements TypeCompilerInterface +{ + public function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $expr) + { + $compiler + ->compilePreconditions($expr->left) + ->compilePreconditions($expr->right) + ; + } + + public function compile(ExpressionCompiler $compiler, ExpressionInterface $expr) + { + $compiler + ->write("(") + ->compileInternal($expr->left) + ->write(") ".$this->getOperator()." (") + ->compileInternal($expr->right) + ->write(")") + ; + } + + abstract protected function getOperator(); +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/ContainerAwareVariableCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/ContainerAwareVariableCompiler.php new file mode 100644 index 0000000..809ffc2 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/ContainerAwareVariableCompiler.php @@ -0,0 +1,60 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\VariableExpressionCompiler; + +class ContainerAwareVariableCompiler extends VariableExpressionCompiler +{ + private $serviceMap = array(); + private $parameterMap = array(); + + public function setMaps(array $serviceMap, array $parameterMap) + { + $this->serviceMap = $serviceMap; + $this->parameterMap = $parameterMap; + } + + public function compile(ExpressionCompiler $compiler, ExpressionInterface $expr) + { + if (isset($this->serviceMap[$expr->name])) { + $compiler->write("\$context['container']->get('{$this->serviceMap[$expr->name]}'"); + + if ($expr->allowNull) { + $compiler->write(", ".ContainerInterface::NULL_ON_INVALID_REFERENCE); + } + + $compiler->write(")"); + + return; + } + + if (isset($this->parameterMap[$expr->name])) { + $compiler->write("\$context['container']->getParameter('{$this->parameterMap[$expr->name]}')"); + + return; + } + + parent::compile($compiler, $expr); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/AuthenticationTrustFunctionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/AuthenticationTrustFunctionCompiler.php new file mode 100644 index 0000000..aa98f6c --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/AuthenticationTrustFunctionCompiler.php @@ -0,0 +1,35 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func; + +use JMS\SecurityExtraBundle\Exception\InvalidArgumentException; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; + +abstract class AuthenticationTrustFunctionCompiler implements FunctionCompilerInterface +{ + public function compilePreconditions(ExpressionCompiler $compiler, FunctionExpression $function) + { + if (!empty($function->args)) { + throw new InvalidArgumentException(sprintf('The '.$this->getName().'() function does not accept any arguments, but got "%s".', var_export($function->args, true))); + } + + $compiler->verifyItem('token', 'Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/FunctionCompilerInterface.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/FunctionCompilerInterface.php new file mode 100644 index 0000000..b364f0b --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/FunctionCompilerInterface.php @@ -0,0 +1,29 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; + +interface FunctionCompilerInterface +{ + function getName(); + function compilePreconditions(ExpressionCompiler $compiler, FunctionExpression $function); + function compile(ExpressionCompiler $compiler, FunctionExpression $function); +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/HasAnyRoleFunctionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/HasAnyRoleFunctionCompiler.php new file mode 100644 index 0000000..02e71fa --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/HasAnyRoleFunctionCompiler.php @@ -0,0 +1,63 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func; + +use JMS\SecurityExtraBundle\Exception\RuntimeException; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; + +class HasAnyRoleFunctionCompiler implements FunctionCompilerInterface +{ + private $rolesExpr; + + public function getName() + { + return 'hasAnyRole'; + } + + public function compilePreconditions(ExpressionCompiler $compiler, FunctionExpression $function) + { + if (0 === count($function->args)) { + throw new RuntimeException('The function hasAnyRole() expects at least one argument, but got none.'); + } + + $this->rolesExpr = $compiler->getRolesExpr(); + } + + public function compile(ExpressionCompiler $compiler, FunctionExpression $function) + { + $compiler->write("("); + + $first = true; + foreach ($function->args as $arg) { + if (!$first) { + $compiler->write(" || "); + } + $first = false; + + $compiler + ->write("isset({$this->rolesExpr}[") + ->compileInternal($arg) + ->write("])") + ; + } + + $compiler->write(")"); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/HasRoleFunctionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/HasRoleFunctionCompiler.php new file mode 100644 index 0000000..77a9834 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/HasRoleFunctionCompiler.php @@ -0,0 +1,51 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func; + +use JMS\SecurityExtraBundle\Exception\RuntimeException; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; + +class HasRoleFunctionCompiler implements FunctionCompilerInterface +{ + private $rolesExpr; + + public function getName() + { + return 'hasRole'; + } + + public function compilePreconditions(ExpressionCompiler $compiler, FunctionExpression $function) + { + if (1 !== count($function->args)) { + throw new RuntimeException(sprintf('The hasRole() function expects exactly one argument, but got "%s".', var_export($function->args, true))); + } + + $this->rolesExpr = $compiler->getRolesExpr(); + } + + public function compile(ExpressionCompiler $compiler, FunctionExpression $function) + { + $compiler + ->write("isset({$this->rolesExpr}[") + ->compileInternal($function->args[0]) + ->write("])") + ; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/IsAnonymousFunctionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/IsAnonymousFunctionCompiler.php new file mode 100644 index 0000000..412825d --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/IsAnonymousFunctionCompiler.php @@ -0,0 +1,39 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; + +class IsAnonymousFunctionCompiler extends AuthenticationTrustFunctionCompiler +{ + public function getName() + { + return 'isAnonymous'; + } + + public function compile(ExpressionCompiler $compiler, FunctionExpression $function) + { + $compiler + ->compileInternal(new VariableExpression('trust_resolver')) + ->write("->isAnonymous(\$context['token'])"); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/IsAuthenticatedFunctionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/IsAuthenticatedFunctionCompiler.php new file mode 100644 index 0000000..ead97d8 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/IsAuthenticatedFunctionCompiler.php @@ -0,0 +1,41 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; + +class IsAuthenticatedFunctionCompiler extends AuthenticationTrustFunctionCompiler +{ + public function getName() + { + return 'isAuthenticated'; + } + + public function compile(ExpressionCompiler $compiler, FunctionExpression $function) + { + $compiler + ->write("!") + ->compileInternal(new VariableExpression('trust_resolver')) + ->write("->isAnonymous(\$context['token'])") + ; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/IsFullyAuthenticatedFunctionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/IsFullyAuthenticatedFunctionCompiler.php new file mode 100644 index 0000000..366ef07 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/IsFullyAuthenticatedFunctionCompiler.php @@ -0,0 +1,39 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; + +class IsFullyAuthenticatedFunctionCompiler extends AuthenticationTrustFunctionCompiler +{ + public function getName() + { + return 'isFullyAuthenticated'; + } + + public function compile(ExpressionCompiler $compiler, FunctionExpression $function) + { + $compiler + ->compileInternal(new VariableExpression('trust_resolver')) + ->write("->isFullFledged(\$context['token'])"); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/IsRememberMeFunctionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/IsRememberMeFunctionCompiler.php new file mode 100644 index 0000000..cbba7c8 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/Func/IsRememberMeFunctionCompiler.php @@ -0,0 +1,39 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; + +class IsRememberMeFunctionCompiler extends AuthenticationTrustFunctionCompiler +{ + public function getName() + { + return 'isRememberMe'; + } + + public function compile(ExpressionCompiler $compiler, FunctionExpression $function) + { + $compiler + ->compileInternal(new VariableExpression('trust_resolver')) + ->write("->isRememberMe(\$context['token'])"); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/IsEqualExpressionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/IsEqualExpressionCompiler.php new file mode 100644 index 0000000..93a8b23 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/IsEqualExpressionCompiler.php @@ -0,0 +1,32 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler; + +class IsEqualExpressionCompiler extends BinaryExprCompiler +{ + public function getType() + { + return 'JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\IsEqualExpression'; + } + + protected function getOperator() + { + return '==='; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/NotExpressionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/NotExpressionCompiler.php new file mode 100644 index 0000000..1fccae8 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/NotExpressionCompiler.php @@ -0,0 +1,44 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface; + +class NotExpressionCompiler implements TypeCompilerInterface +{ + public function getType() + { + return 'JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\NotExpression'; + } + + public function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $expr) + { + $compiler->compilePreconditions($expr->expr); + } + + public function compile(ExpressionCompiler $compiler, ExpressionInterface $expr) + { + $compiler + ->write('!(') + ->compileInternal($expr->expr) + ->write(')') + ; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/OrExpressionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/OrExpressionCompiler.php new file mode 100644 index 0000000..4a4554e --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/OrExpressionCompiler.php @@ -0,0 +1,32 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler; + +class OrExpressionCompiler extends BinaryExprCompiler +{ + public function getType() + { + return 'JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\OrExpression'; + } + + protected function getOperator() + { + return '||'; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/ParameterExpressionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/ParameterExpressionCompiler.php new file mode 100644 index 0000000..678a119 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/ParameterExpressionCompiler.php @@ -0,0 +1,80 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\TypeCompilerInterface; + +class ParameterExpressionCompiler implements TypeCompilerInterface +{ + public function getType() + { + return 'JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ParameterExpression'; + } + + public function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $parameter) + { + $compiler->verifyItem('object', 'CG\Proxy\MethodInvocation'); + + if (!isset($compiler->attributes['parameter_mapping_name'])) { + $this->addParameterMapping($compiler); + } + + $compiler + ->writeln("if (!isset(\${$compiler->attributes['parameter_mapping_name']}['{$parameter->name}'])) {") + ->indent() + ->write("throw new RuntimeException(sprintf('There is no parameter with name \"{$parameter->name}\" for method \"%s\".', ") + ->compileInternal(new VariableExpression('object')) + ->writeln("));") + ->outdent() + ->write("}\n\n") + ; + } + + public function compile(ExpressionCompiler $compiler, ExpressionInterface $parameter) + { + $compiler + ->compileInternal(new VariableExpression('object')) + ->write("->arguments[") + ->write("\${$compiler->attributes['parameter_mapping_name']}") + ->write("['{$parameter->name}']]") + ; + } + + private function addParameterMapping(ExpressionCompiler $compiler) + { + $name = $compiler->nextName(); + $indexName = $compiler->nextName(); + $paramName = $compiler->nextName(); + + $compiler + ->setAttribute('parameter_mapping_name', $name) + ->writeln("\$$name = array();") + ->write("foreach (") + ->compileInternal(new VariableExpression('object')) + ->writeln("->reflection->getParameters() as \$$indexName => \$$paramName) {") + ->indent() + ->writeln("\${$name}[\${$paramName}->name] = \$$indexName;") + ->outdent() + ->writeln("}\n") + ; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/TypeCompilerInterface.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/TypeCompilerInterface.php new file mode 100644 index 0000000..949a631 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/TypeCompilerInterface.php @@ -0,0 +1,29 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; + +interface TypeCompilerInterface +{ + function getType(); + function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $expr); + function compile(ExpressionCompiler $compiler, ExpressionInterface $expr); +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/VariableExpressionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/VariableExpressionCompiler.php new file mode 100644 index 0000000..ad1841c --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Compiler/VariableExpressionCompiler.php @@ -0,0 +1,74 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionCompiler; + +class VariableExpressionCompiler implements TypeCompilerInterface +{ + public function getType() + { + return 'JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression'; + } + + public function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $expr) + { + if ('user' === $expr->name) { + $compiler + ->setAttribute('user_var_name', $name = $compiler->nextName()) + ->write("\$$name = ") + ->compileInternal(new VariableExpression('token')) + ->write("->getUser();\n\n") + ; + } + } + + public function compile(ExpressionCompiler $compiler, ExpressionInterface $expr) + { + if ('permitAll' === $expr->name) { + $compiler->write('true'); + + return; + } + + if ('denyAll' === $expr->name) { + $compiler->write('false'); + + return; + } + + if ('user' === $expr->name) { + $compiler->write("\${$compiler->attributes['user_var_name']}"); + + return; + } + + if ($expr->allowNull) { + $compiler->write("(isset(\$context['{$expr->name}']) ? "); + } + + $compiler->write("\$context['{$expr->name}']"); + + if ($expr->allowNull) { + $compiler->write(" : null)"); + } + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ContainerAwareExpressionHandler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ContainerAwareExpressionHandler.php new file mode 100644 index 0000000..f2d29af --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ContainerAwareExpressionHandler.php @@ -0,0 +1,47 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionHandlerInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Lazy-loading container aware expression handler. + * + * @author Johannes M. Schmitt + */ +class ContainerAwareExpressionHandler implements ExpressionHandlerInterface +{ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + public function createContext(TokenInterface $token, $object) + { + return array( + 'container' => $this->container, + 'token' => $token, + 'object' => $object, + ); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/DefaultExpressionHandler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/DefaultExpressionHandler.php new file mode 100644 index 0000000..70e4e13 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/DefaultExpressionHandler.php @@ -0,0 +1,51 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression; + +use Symfony\Component\Security\Core\Role\RoleHierarchyInterface; +use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +class DefaultExpressionHandler implements ExpressionHandlerInterface +{ + private $trustResolver; + private $roleHierarchy; + + public function __construct(AuthenticationTrustResolverInterface $trustResolver, + RoleHierarchyInterface $roleHierarchy = null) + { + $this->trustResolver = $trustResolver; + $this->roleHierarchy = $roleHierarchy; + } + + public function createContext(TokenInterface $token, $object) + { + $context = array( + 'token' => $token, + 'object' => $object, + 'trust_resolver' => $this->trustResolver, + ); + + if (null !== $this->roleHierarchy) { + $context['role_hierarchy'] = $this->roleHierarchy; + } + + return $context; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Expression.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Expression.php new file mode 100644 index 0000000..219218d --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/Expression.php @@ -0,0 +1,40 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression; + +final class Expression +{ + /** READ-ONLY */ + public $expression; + + public function __construct($expression) + { + $this->expression = $expression; + } + + public function getHashCode() + { + return sha1($this->expression); + } + + public function __toString() + { + return 'EXPRESSION('.$this->expression.')'; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionCompiler.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionCompiler.php new file mode 100644 index 0000000..fbfc69a --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionCompiler.php @@ -0,0 +1,395 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression; + +use JMS\SecurityExtraBundle\Exception\RuntimeException; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\TypeCompilerInterface; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\IsEqualExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func\FunctionCompilerInterface; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\OrExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\MethodCallExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\GetPropertyExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\GetItemExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ConstantExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ArrayExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\AndExpression; + +class ExpressionCompiler +{ + public $attributes = array(); + + private $indentationLevel = 0; + private $indentationSpaces = 4; + + private $nameCount = 0; + private $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + private $charCount = 52; + private $reservedNames = array('context' => true); + + private $itemExists = array(); + private $itemType = array(); + private $rolesName; + + private $code; + private $parser; + private $typeCompilers; + private $functionCompilers; + + public function __construct() + { + $this->addTypeCompiler(new Compiler\AndExpressionCompiler()); + $this->addTypeCompiler(new Compiler\IsEqualExpressionCompiler()); + $this->addTypeCompiler(new Compiler\OrExpressionCompiler()); + $this->addTypeCompiler(new Compiler\VariableExpressionCompiler()); + $this->addTypeCompiler(new Compiler\NotExpressionCompiler()); + + $this->functionCompilers = array( + 'isAnonymous' => new Compiler\Func\IsAnonymousFunctionCompiler(), + 'isAuthenticated' => new Compiler\Func\IsAuthenticatedFunctionCompiler(), + 'isRememberMe' => new Compiler\Func\IsRememberMeFunctionCompiler(), + 'isFullyAuthenticated' => new Compiler\Func\IsFullyAuthenticatedFunctionCompiler(), + 'hasRole' => new Compiler\Func\HasRoleFunctionCompiler(), + 'hasAnyRole' => new Compiler\Func\HasAnyRoleFunctionCompiler(), + ); + } + + public function setAttribute($name, $value) + { + $this->attributes[$name] = $value; + + return $this; + } + + public function addTypeCompiler(TypeCompilerInterface $compiler) + { + $this->typeCompilers[$compiler->getType()] = $compiler; + } + + public function addFunctionCompiler(FunctionCompilerInterface $compiler) + { + $this->functionCompilers[$compiler->getName()] = $compiler; + } + + public function compileExpression(Expression $expr) + { + return $this->compile($this->getParser()->parse($expr->expression), + $expr->expression); + } + + public function compile(ExpressionInterface $expr, $raw = null) + { + $this->nameCount = 0; + $this->code = ''; + $this->itemExists = $this->itemType = $this->attributes = array(); + $this->rolesName = null; + + if ($raw) { + $this->writeln('// Expression: '.$raw); + } + + $this + ->writeln('return function(array $context) {') + ->indent() + ->compilePreconditions($expr) + ->write('return ') + ->compileInternal($expr) + ->writeln(';') + ->outdent() + ->writeln('};') + ; + + return $this->code; + } + + public function indent() + { + $this->indentationLevel += 1; + + return $this; + } + + public function outdent() + { + $this->indentationLevel -= 1; + + if ($this->indentationLevel < 0) { + throw new RuntimeException('The identation level cannot be less than zero.'); + } + + return $this; + } + + public function writeln($content) + { + $this->write($content."\n"); + + return $this; + } + + public function getRolesExpr() + { + if (null !== $this->rolesName) { + return '$'.$this->rolesName; + } + + $this->verifyItem('token', 'Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + $this->rolesName = $rolesName = $this->nextName(); + $hierarchyName = $this->nextName(); + $tmpName = $this->nextName(); + $this + ->writeln("\$$rolesName = \$context['token']->getRoles();") + ->write("if (null !== \$$hierarchyName = ") + ->compileInternal(new VariableExpression('role_hierarchy', true)) + ->writeln(") {") + ->indent() + ->writeln("\$$rolesName = \${$hierarchyName}->getReachableRoles(\$$rolesName);") + ->outdent() + ->write("}\n\n") + ->writeln("\$$tmpName = array();") + ->writeln("foreach (\$$rolesName as \$role) {") + ->indent() + ->writeln("\${$tmpName}[\$role->getRole()] = true;") + ->outdent() + ->writeln("}") + ->write("\$$rolesName = \$$tmpName;\n\n") + ; + + return '$'.$rolesName; + } + + public function verifyItem($key, $expectedType = null) + { + if (!isset($this->itemExists[$key])) { + $this->itemExists[$key] = true; + + $this + ->writeln("if (!isset(\$context['$key'])) {") + ->indent() + ->writeln("throw new RuntimeException('The context contains no item with key \"$key\".');") + ->outdent() + ->write("}\n\n") + ; + } + + if (null !== $expectedType) { + if (isset($this->itemType[$key])) { + if ($this->itemType[$key] !== $expectedType) { + throw new RuntimeException(sprintf('Cannot verify that item "%s" is of type "%s" because it is already expected to be of type "%s".', + $key, $expectedType, $this->itemType[$key] + )); + } + + return $this; + } + + $this + ->writeln("if (!\$context['$key'] instanceof $expectedType) {") + ->indent() + ->writeln("throw new RuntimeException(sprintf('The item \"$key\" is expected to be of type \"$expectedType\", but got \"%s\".', get_class(\$context['$key'])));") + ->outdent() + ->write("}\n\n") + ; + } + + return $this; + } + + public function write($content) + { + $lines = explode("\n", $content); + for ($i=0,$c=count($lines); $i<$c; $i++) { + if ($this->indentationLevel > 0 + && !empty($lines[$i]) + && (empty($this->code) || "\n" === substr($this->code, -1))) { + $this->code .= str_repeat(' ', $this->indentationLevel * $this->indentationSpaces); + } + + $this->code .= $lines[$i]; + + if ($i+1 < $c) { + $this->code .= "\n"; + } + } + + return $this; + } + + public function nextName() + { + while (true) { + $name = ''; + $i = $this->nameCount; + + $name .= $this->chars[$i % $this->charCount]; + $i = intval($i / $this->charCount); + + while ($i > 0) { + $i -= 1; + $name .= $this->chars[$i % $this->charCount]; + $i = intval($i / $this->charCount); + } + + $this->nameCount += 1; + + // check that the name is not reserved + if (isset($this->reservedNames[$name])) { + continue; + } + + return $name; + } + } + + public function compilePreconditions(ExpressionInterface $expr) + { + if ($typeCompiler = $this->findTypeCompiler(get_class($expr))) { + $typeCompiler->compilePreconditions($this, $expr); + + return $this; + } + + if ($expr instanceof FunctionExpression) { + $this->getFunctionCompiler($expr->name)->compilePreconditions($this, $expr); + + foreach ($expr->args as $arg) { + $this->compilePreconditions($arg); + } + + return $this; + } + + if ($expr instanceof VariableExpression) { + $this->getVariableCompiler($expr->name)->compilePreconditions($this, $expr); + + return $this; + } + + if ($expr instanceof MethodCallExpression) { + $this->compilePreconditions($expr->object); + + foreach ($expr->args as $arg) { + $this->compilePreconditions($arg); + } + + return $this; + } + + if ($expr instanceof GetPropertyExpression) { + $this->compilePreconditions($expr->object); + + return $this; + } + + return $this; + } + + public function compileInternal(ExpressionInterface $expr) + { + if ($typeCompiler = $this->findTypeCompiler(get_class($expr))) { + $typeCompiler->compile($this, $expr); + + return $this; + } + + if ($expr instanceof ArrayExpression) { + $this->code .= 'array('; + foreach ($expr->elements as $key => $value) { + $this->code .= var_export($key, true).' => '; + $this->compileInternal($value); + $this->code .= ','; + } + $this->code .= ')'; + + return $this; + } + + if ($expr instanceof ConstantExpression) { + $this->code .= var_export($expr->value, true); + + return $this; + } + + if ($expr instanceof FunctionExpression) { + $this->getFunctionCompiler($expr->name)->compile($this, $expr); + + return $this; + } + + if ($expr instanceof GetItemExpression) { + $this->compileInternal($expr->array); + $this->code .= '['.$expr->key.']'; + + return $this; + } + + if ($expr instanceof GetPropertyExpression) { + $this->compileInternal($expr->object); + $this->code .= '->'.$expr->name; + + return $this; + } + + if ($expr instanceof MethodCallExpression) { + $this->compileInternal($expr->object); + $this->code .= '->'.$expr->method.'('; + + $first = true; + foreach ($expr->args as $arg) { + if (!$first) { + $this->code .= ', '; + } + $first = false; + + $this->compileInternal($arg); + } + $this->code .= ')'; + + return $this; + } + + throw new RuntimeException(sprintf('Unknown expression "%s".', get_class($expr))); + } + + public function getFunctionCompiler($name) + { + if (!isset($this->functionCompilers[$name])) { + throw new RuntimeException(sprintf('There is no compiler for function "%s".', $name)); + } + + return $this->functionCompilers[$name]; + } + + private function findTypeCompiler($type) + { + return isset($this->typeCompilers[$type]) ? $this->typeCompilers[$type] : null; + } + + private function getParser() + { + if (null !== $this->parser) { + return $this->parser; + } + + return $this->parser = new ExpressionParser(); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionHandlerInterface.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionHandlerInterface.php new file mode 100644 index 0000000..24e6024 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionHandlerInterface.php @@ -0,0 +1,26 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +interface ExpressionHandlerInterface +{ + function createContext(TokenInterface $token, $object); +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionLexer.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionLexer.php new file mode 100644 index 0000000..e1f4445 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionLexer.php @@ -0,0 +1,134 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression; + +use JMS\SecurityExtraBundle\Exception\InvalidArgumentException; + +final class ExpressionLexer +{ + public $token; + public $lookahead; + + private $tokens; + private $pointer; + + const T_STRING = 1; + const T_IDENTIFIER = 2; + const T_NONE = 3; + const T_COMMA = 4; + const T_OPEN_PARENTHESIS = 5; + const T_CLOSE_PARENTHESIS = 6; + const T_AND = 7; + const T_OR = 8; + const T_PARAMETER = 9; + const T_OBJECT_OPERATOR = 10; + const T_OPEN_BRACKET = 11; + const T_CLOSE_BRACKET = 12; + const T_OPEN_BRACE = 13; + const T_CLOSE_BRACE = 14; + const T_COLON = 15; + const T_IS_EQUAL = 16; + const T_NOT = 17; + + public static function getLiteral($type) + { + static $constants; + + if (null === $constants) { + $ref = new \ReflectionClass(get_called_class()); + $constants = $ref->getConstants(); + } + + if (false === $literal = array_search($type, $constants, true)) { + throw new InvalidArgumentException(sprintf('There is no token of value "%s".', $type)); + } + + return $literal; + } + + public function initialize($input) + { + static $pattern = '/(#?[a-z][a-z0-9]*|\'(?:[^\']|(?<=\\\\)\')*\'|"(?:[^"]|(?<=\\\\)")*"|&&|\|\||==)|\s+|(.)/i'; + + $parts = preg_split($pattern, $input, -1, PREG_SPLIT_OFFSET_CAPTURE + | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + + $tokens = array(); + foreach ($parts as $part) { + list($value, $position) = $part; + $type = self::T_NONE; + + if ("'" === $value[0] || '"' === $value[0]) { + $type = self::T_STRING; + $value = substr($value, 1, -1); + } else if (',' === $value) { + $type = self::T_COMMA; + } else if ('(' === $value) { + $type = self::T_OPEN_PARENTHESIS; + } else if (')' === $value) { + $type = self::T_CLOSE_PARENTHESIS; + } else if ('[' === $value) { + $type = self::T_OPEN_BRACKET; + } else if (']' === $value) { + $type = self::T_CLOSE_BRACKET; + } else if ('{' === $value) { + $type = self::T_OPEN_BRACE; + } else if ('}' === $value) { + $type = self::T_CLOSE_BRACE; + } else if ('&&' === $value || 'and' === strtolower($value)) { + $type = self::T_AND; + } else if ('||' === $value || 'or' === strtolower($value)) { + $type = self::T_OR; + } else if ('!' === $value || 'not' === strtolower($value)) { + $type = self::T_NOT; + } else if (':' === $value) { + $type = self::T_COLON; + } else if ('.' === $value) { + $type = self::T_OBJECT_OPERATOR; + } else if ('==' === $value) { + $type = self::T_IS_EQUAL; + } else if ('#' === $value[0]) { + $type = self::T_PARAMETER; + $value = substr($value, 1); + } else if (ctype_alpha($value)) { + $type = self::T_IDENTIFIER; + } + + $tokens[] = array( + 'type' => $type, + 'value' => $value, + 'position' => $position, + ); + } + + $this->tokens = $tokens; + $this->pointer = -1; + $this->next(); + } + + public function next() + { + $this->pointer += 1; + $this->token = $this->lookahead; + $this->lookahead = isset($this->tokens[$this->pointer]) ? + $this->tokens[$this->pointer] : null; + + return $this->lookahead !== null; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionParser.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionParser.php new file mode 100644 index 0000000..98fd32e --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionParser.php @@ -0,0 +1,290 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\NotExpression; + +use JMS\SecurityExtraBundle\Exception\RuntimeException; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\IsEqualExpression; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ParameterExpression; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\VariableExpression; + +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ConstantExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\OrExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\AndExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ArrayExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\GetItemExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\GetPropertyExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\MethodCallExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\ExpressionInterface; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression; + +final class ExpressionParser +{ + const PRECEDENCE_OR = 10; + const PRECEDENCE_AND = 15; + const PRECEDENCE_IS_EQUAL = 20; + const PRECEDENCE_NOT = 30; + + private $lexer; + + public function __construct() + { + $this->lexer = new ExpressionLexer(); + } + + public function parse($str) + { + $this->lexer->initialize($str); + + return $this->Expression(); + } + + private function Expression($precedence = 0) + { + $expr = $this->Primary(); + + while (true) { + if (ExpressionLexer::T_AND === $this->lexer->lookahead['type'] + && $precedence <= self::PRECEDENCE_AND) { + $this->lexer->next(); + + $expr = new AndExpression($expr, $this->Expression( + self::PRECEDENCE_AND + 1)); + continue; + } + + if (ExpressionLexer::T_OR === $this->lexer->lookahead['type'] + && $precedence <= self::PRECEDENCE_OR) { + $this->lexer->next(); + + $expr = new OrExpression($expr, $this->Expression( + self::PRECEDENCE_OR + 1)); + continue; + } + + if (ExpressionLexer::T_IS_EQUAL === $this->lexer->lookahead['type'] + && $precedence <= self::PRECEDENCE_IS_EQUAL) { + $this->lexer->next(); + + $expr = new IsEqualExpression($expr, $this->Expression( + self::PRECEDENCE_IS_EQUAL + 1)); + continue; + } + + break; + } + + return $expr; + } + + private function Primary() + { + if (ExpressionLexer::T_NOT === $this->lexer->lookahead['type']) { + $this->lexer->next(); + $expr = new NotExpression($this->Expression(self::PRECEDENCE_NOT)); + + return $this->Suffix($expr); + } + + if (ExpressionLexer::T_OPEN_PARENTHESIS === $this->lexer->lookahead['type']) { + $this->lexer->next(); + $expr = $this->Expression(); + $this->match(ExpressionLexer::T_CLOSE_PARENTHESIS); + + return $this->Suffix($expr); + } + + if (ExpressionLexer::T_STRING === $this->lexer->lookahead['type']) { + return new ConstantExpression($this->match(ExpressionLexer::T_STRING)); + } + + if (ExpressionLexer::T_OPEN_BRACE === $this->lexer->lookahead['type']) { + return $this->Suffix($this->MapExpr()); + } + + if (ExpressionLexer::T_OPEN_BRACKET === $this->lexer->lookahead['type']) { + return $this->Suffix($this->ListExpr()); + } + + if (ExpressionLexer::T_IDENTIFIER === $this->lexer->lookahead['type']) { + $name = $this->match(ExpressionLexer::T_IDENTIFIER); + + if (ExpressionLexer::T_OPEN_PARENTHESIS === $this->lexer->lookahead['type']) { + $args = $this->Arguments(); + + return $this->Suffix(new FunctionExpression($name, $args)); + } + + return $this->Suffix(new VariableExpression($name)); + } + + if (ExpressionLexer::T_PARAMETER === $this->lexer->lookahead['type']) { + return $this->Suffix(new ParameterExpression($this->match(ExpressionLexer::T_PARAMETER))); + } + + $this->error('primary expression'); + } + + private function ListExpr() + { + $this->match(ExpressionLexer::T_OPEN_BRACKET); + + $elements = array(); + while (ExpressionLexer::T_CLOSE_BRACKET !== $this->lexer->lookahead['type']) { + $elements[] = $this->Expression(); + + if (ExpressionLexer::T_COMMA !== $this->lexer->lookahead['type']) { + break; + } + $this->lexer->next(); + } + + $this->match(ExpressionLexer::T_CLOSE_BRACKET); + + return new ArrayExpression($elements); + } + + private function MapExpr() + { + $this->match(ExpressionLexer::T_OPEN_BRACE); + + $entries = array(); + while (ExpressionLexer::T_CLOSE_BRACE !== $this->lexer->lookahead['type']) { + $key = $this->match(ExpressionLexer::T_STRING); + $this->match(ExpressionLexer::T_COLON); + $entries[$key] = $this->Expression(); + + if (ExpressionLexer::T_COMMA !== $this->lexer->lookahead['type']) { + break; + } + + $this->lexer->next(); + } + + $this->match(ExpressionLexer::T_CLOSE_BRACE); + + return new ArrayExpression($entries); + } + + private function Suffix(ExpressionInterface $expr) + { + while (true) { + if (ExpressionLexer::T_OBJECT_OPERATOR === $this->lexer->lookahead['type']) { + $this->lexer->next(); + $name = $this->match(ExpressionLexer::T_IDENTIFIER); + + if (ExpressionLexer::T_OPEN_PARENTHESIS === $this->lexer->lookahead['type']) { + $args = $this->Arguments(); + $expr = new MethodCallExpression($expr, $name, $args); + continue; + } + + $expr = new GetPropertyExpression($expr, $name); + continue; + } + + if (ExpressionLexer::T_OPEN_BRACKET === $this->lexer->lookahead['type']) { + $this->lexer->next(); + $key = $this->Expression(); + $this->match(ExpressionLexer::T_CLOSE_BRACKET); + $expr = new GetItemExpression($expr, $key); + continue; + } + + break; + } + + return $expr; + } + + private function FunctionCall() + { + $name = $this->match(ExpressionLexer::T_IDENTIFIER); + $args = $this->Arguments(); + + return new FunctionExpression($name, $args); + } + + private function Arguments() + { + $this->match(ExpressionLexer::T_OPEN_PARENTHESIS); + $args = array(); + + while (ExpressionLexer::T_CLOSE_PARENTHESIS !== $this->lexer->lookahead['type']) { + $args[] = $this->Expression(); + + if (ExpressionLexer::T_COMMA !== $this->lexer->lookahead['type']) { + break; + } + + $this->match(ExpressionLexer::T_COMMA); + } + $this->match(ExpressionLexer::T_CLOSE_PARENTHESIS); + + return $args; + } + + private function Value() + { + return $this->matchAny(array(ExpressionLexer::T_STRING)); + } + + private function matchAny(array $types) + { + if (null !== $this->lexer->lookahead) { + foreach ($types as $type) { + if ($type === $this->lexer->lookahead['type']) { + $this->lexer->next(); + + return $this->lexer->token['value']; + } + } + } + + $this->error(sprintf('one of these tokens "%s"', + implode('", "', array_map(array('JMS\SecurityExtraBundle\Security\Authorization\Expression\Lexer', 'getLiteral'), $types)) + )); + } + + private function match($type) + { + if (null === $this->lexer->lookahead + || $type !== $this->lexer->lookahead['type']) { + $this->error(sprintf('token "%s"', ExpressionLexer::getLiteral($type))); + } + + $this->lexer->next(); + + return $this->lexer->token['value']; + } + + private function error($expected) + { + $actual = null === $this->lexer->lookahead ? 'end of file' + : sprintf('token "%s" with value "%s" at position %d', + ExpressionLexer::getLiteral($this->lexer->lookahead['type']), + $this->lexer->lookahead['value'], + $this->lexer->lookahead['position']); + + throw new RuntimeException(sprintf('Expected %s, but got %s.', $expected, $actual)); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionVoter.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionVoter.php new file mode 100644 index 0000000..cf04742 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/ExpressionVoter.php @@ -0,0 +1,114 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression; + +use JMS\SecurityExtraBundle\Exception\RuntimeException; +use Symfony\Component\Security\Core\Role\RoleHierarchyInterface; +use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; + +/** + * Expression-based voter. + * + * This voter allows to use complex access expression in a high-performance + * way. This is the preferred voter for any non-simple access checks. + * + * @author Johannes M. Schmitt + */ +class ExpressionVoter implements VoterInterface +{ + private $evaluators = array(); + private $compiler; + private $cacheDir; + private $expressionHandler; + + public function __construct(ExpressionHandlerInterface $expressionHandler) { + $this->expressionHandler = $expressionHandler; + } + + public function setCacheDir($cacheDir) + { + $this->cacheDir = $cacheDir; + } + + public function setCompiler(ExpressionCompiler $compiler) + { + $this->compiler = $compiler; + } + + public function vote(TokenInterface $token, $object, array $attributes) + { + $result = VoterInterface::ACCESS_ABSTAIN; + + foreach ($attributes as $attribute) { + if (!$attribute instanceof Expression) { + continue; + } + + $result = VoterInterface::ACCESS_DENIED; + if (!isset($this->evaluators[$attribute->expression])) { + $this->evaluators[$attribute->expression] = + $this->createEvaluator($attribute); + } + + if (call_user_func($this->evaluators[$attribute->expression], + $this->expressionHandler->createContext($token, $object))) { + return VoterInterface::ACCESS_GRANTED; + } + } + + return $result; + } + + public function supportsAttribute($attribute) + { + return $attribute instanceof Expression; + } + + public function supportsClass($class) + { + return true; + } + + protected function getCompiler() + { + if (null === $this->compiler) { + throw new RuntimeException('A compiler must be set.'); + } + + return $this->compiler; + } + + private function createEvaluator(Expression $expr) + { + if ($this->cacheDir) { + if (is_file($file = $this->cacheDir.'/'.sha1($expr->expression).'.php')) { + return require $file; + } + + $source = $this->getCompiler()->compileExpression($expr); + file_put_contents($file, "getCompiler()->compileExpression($expr)); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/LazyLoadingExpressionVoter.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/LazyLoadingExpressionVoter.php new file mode 100644 index 0000000..b9d577e --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Expression/LazyLoadingExpressionVoter.php @@ -0,0 +1,39 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Expression; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\ExpressionVoter; + +class LazyLoadingExpressionVoter extends ExpressionVoter +{ + private $container; + private $compilerId; + + public function setLazyCompiler(ContainerInterface $container, $id) + { + $this->container = $container; + $this->compilerId = $id; + } + + protected function getCompiler() + { + return $this->container->get($this->compilerId); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Interception/MethodSecurityInterceptor.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Interception/MethodSecurityInterceptor.php new file mode 100644 index 0000000..8d4c63a --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Interception/MethodSecurityInterceptor.php @@ -0,0 +1,150 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Interception; + +use JMS\SecurityExtraBundle\Exception\RuntimeException; +use CG\Core\ClassUtils; + +use CG\Proxy\MethodInterceptorInterface; +use CG\Proxy\MethodInvocation; +use JMS\SecurityExtraBundle\Metadata\MethodMetadata; +use JMS\SecurityExtraBundle\Security\Authentication\Token\RunAsUserToken; +use JMS\SecurityExtraBundle\Security\Authorization\AfterInvocation\AfterInvocationManagerInterface; +use JMS\SecurityExtraBundle\Security\Authorization\RunAsManagerInterface; +use Metadata\MetadataFactoryInterface; +use Symfony\Component\HttpKernel\Log\LoggerInterface; +use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; +use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; +use Symfony\Component\Security\Core\SecurityContext; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; + +/** + * All invocations of secure methods will go through this class. + * + * @author Johannes M. Schmitt + */ +class MethodSecurityInterceptor implements MethodInterceptorInterface +{ + private $alwaysAuthenticate; + private $securityContext; + private $metadataFactory; + private $authenticationManager; + private $accessDecisionManager; + private $afterInvocationManager; + private $runAsManager; + private $logger; + + public function __construct(SecurityContext $securityContext, AuthenticationManagerInterface $authenticationManager, AccessDecisionManagerInterface $accessDecisionManager, + AfterInvocationManagerInterface $afterInvocationManager, RunAsManagerInterface $runAsManager, MetadataFactoryInterface $metadataFactory, LoggerInterface $logger = null) + { + $this->alwaysAuthenticate = false; + $this->securityContext = $securityContext; + $this->metadataFactory = $metadataFactory; + $this->authenticationManager = $authenticationManager; + $this->accessDecisionManager = $accessDecisionManager; + $this->afterInvocationManager = $afterInvocationManager; + $this->runAsManager = $runAsManager; + $this->logger = $logger; + } + + public function setAlwaysAuthenticate($boolean) + { + $this->alwaysAuthenticate = !!$boolean; + } + + public function intercept(MethodInvocation $method) + { + $metadata = $this->metadataFactory->getMetadataForClass($method->reflection->class); + + // no security metadata, proceed + if (empty($metadata) || !isset($metadata->methodMetadata[$method->reflection->name])) { + return $method->proceed(); + } + $metadata = $metadata->methodMetadata[$method->reflection->name]; + + if (null === $token = $this->securityContext->getToken()) { + throw new AuthenticationCredentialsNotFoundException( + 'The security context was not populated with a Token.' + ); + } + + if ($this->alwaysAuthenticate || !$token->isAuthenticated()) { + $token = $this->authenticationManager->authenticate($token); + $this->securityContext->setToken($token); + } + + if (!empty($metadata->roles) && false === $this->accessDecisionManager->decide($token, $metadata->roles, $method)) { + throw new AccessDeniedException('Token does not have the required roles.'); + } + + if (!empty($metadata->paramPermissions)) { + foreach ($method->arguments as $index => $argument) { + if (null !== $argument && isset($metadata->paramPermissions[$index]) && false === $this->accessDecisionManager->decide($token, $metadata->paramPermissions[$index], $argument)) { + throw new AccessDeniedException(sprintf('Token does not have the required permissions for method "%s::%s".', $method->reflection->class, $method->reflection->name)); + } + } + } + + $runAsToken = null; + if (!empty($metadata->runAsRoles)) { + $runAsToken = $this->runAsManager->buildRunAs($token, $method, $metadata->runAsRoles); + + if (null !== $this->logger) { + $this->logger->debug('Populating security context with RunAsToken'); + } + + if (null === $runAsToken) { + throw new RuntimeException('RunAsManager must not return null from buildRunAs().'); + } + + $this->securityContext->setToken($runAsToken); + } + + try { + $returnValue = $method->proceed(); + + if (null !== $runAsToken) { + $this->restoreOriginalToken($runAsToken); + } + + if (empty($metadata->returnPermissions)) { + return $returnValue; + } + + return $this->afterInvocationManager->decide($this->securityContext->getToken(), $method, $metadata->returnPermissions, $returnValue); + } catch (\Exception $failed) { + if (null !== $runAsToken) { + $this->restoreOriginalToken($runAsToken); + } + + throw $failed; + } + } + + private function restoreOriginalToken(RunAsUserToken $runAsToken) + { + if (null !== $this->logger) { + $this->logger->debug('Populating security context with original Token.'); + } + + $this->securityContext->setToken($runAsToken->getOriginalToken()); + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Interception/SecurityPointcut.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Interception/SecurityPointcut.php new file mode 100644 index 0000000..9cba96c --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Interception/SecurityPointcut.php @@ -0,0 +1,92 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Interception; + +use CG\Core\ClassUtils; +use Metadata\MetadataFactoryInterface; +use JMS\AopBundle\Aop\PointcutInterface; + +class SecurityPointcut implements PointcutInterface +{ + private $metadataFactory; + private $secureAllServices; + private $securedClasses = array(); + private $patterns; + + public function __construct(MetadataFactoryInterface $metadataFactory, $secureAllServices = false, array $patterns = array()) + { + $this->metadataFactory = $metadataFactory; + $this->secureAllServices = $secureAllServices; + $this->patterns = $patterns; + } + + public function setSecuredClasses(array $classes) + { + $this->securedClasses = $classes; + } + + public function matchesClass(\ReflectionClass $class) + { + if ($this->secureAllServices) { + return true; + } + + if ('Controller' === substr(ClassUtils::getUserClass($class->name), -10)) { + return true; + } + + foreach ($this->patterns as $pattern => $expr) { + // if not for all patterns the class is specified, then we need to scan all + // classes to catch all methods + if (false === $pos = strpos($pattern, '::')) { + // controller notation is already checked by JMSDiExtraBundle, + // we can safely ignore these patterns here + if (2 === substr_count($pattern, ':')) { + continue; + } + + return true; + } + + if (0 < preg_match('#'.substr($pattern, 0, $pos).'$#', $class->name)) { + return true; + } + } + + foreach ($this->securedClasses as $securedClass) { + if ($class->name === $securedClass || $class->isSubclassOf($securedClass)) { + return true; + } + } + + return false; + } + + public function matchesMethod(\ReflectionMethod $method) + { + $userClass = ClassUtils::getUserClass($method->class); + $metadata = $this->metadataFactory->getMetadataForClass($userClass); + + if (null === $metadata) { + return false; + } + + return isset($metadata->methodMetadata[$method->name]); + } +} diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/RunAsManager.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/RunAsManager.php new file mode 100644 index 0000000..f1884df --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/RunAsManager.php @@ -0,0 +1,79 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization; + +use JMS\SecurityExtraBundle\Security\Authentication\Token\RunAsUserToken; +use Symfony\Component\Security\Core\Role\Role; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * The RunAsManager creates throw-away Tokens which are temporarily injected into + * the security context for the duration of the invocation of a specific method. + * + * @author Johannes M. Schmitt + */ +class RunAsManager implements RunAsManagerInterface +{ + private $key; + private $rolePrefix; + + public function __construct($key, $rolePrefix = 'ROLE_') + { + $this->key = $key; + $this->rolePrefix = $rolePrefix; + } + + /** + * {@inheritDoc} + */ + public function buildRunAs(TokenInterface $token, $secureObject, array $attributes) + { + $roles = array(); + foreach ($attributes as $attribute) + { + if ($this->supportsAttribute($attribute)) { + $roles[] = new Role($attribute); + } + } + + if (0 === count($roles)) { + return null; + } + + $roles = array_merge($roles, $token->getRoles()); + + return new RunAsUserToken($this->key, $token->getUser(), $token->getCredentials(), $roles, $token); + } + + /** + * {@inheritDoc} + */ + public function supportsAttribute($attribute) + { + return !empty($attribute) && 0 === strpos($attribute, $this->rolePrefix); + } + + /** + * {@inheritDoc} + */ + public function supportsClass($className) + { + return true; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/RunAsManagerInterface.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/RunAsManagerInterface.php new file mode 100644 index 0000000..a2b4909 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/RunAsManagerInterface.php @@ -0,0 +1,57 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * RunAsManagerInterface + * + * @author Johannes M. Schmitt + */ +interface RunAsManagerInterface +{ + /** + * Creates a temporary RunAsToken. + * + * The returned Token must have a complementing AuthenticationProvider implementation. + * + * @param TokenInterface $token the original Token + * @param object $secureObject the secure object which caused this call + * @param array $attributes an array of attributes to apply to the built token + * @return TokenInterface + */ + function buildRunAs(TokenInterface $token, $secureObject, array $attributes); + + /** + * Whether this RunAsManager supports the given attribute + * + * @param string $attribute + * @return Boolean + */ + function supportsAttribute($attribute); + + /** + * Whether this RunAsManager supports the given class. + * + * @param string $className The class of the secure object which requests RunAs capabilities + * @return Boolean + */ + function supportsClass($className); +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Voter/IddqdVoter.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Voter/IddqdVoter.php new file mode 100644 index 0000000..663cd4e --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Voter/IddqdVoter.php @@ -0,0 +1,61 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\SecurityExtraBundle\Security\Authorization\Voter; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; + +/** + * This voter adds a special role "ROLE_IDDQD" which effectively bypasses any, + * and all security checks. + * + * Most of the time, you will want to use this rule in combination with a + * @RunAs annotation to disable security checks for the invocation of a + * specific method. + * + * @author Johannes M. Schmitt + */ +class IddqdVoter implements VoterInterface +{ + public function vote(TokenInterface $token, $object, array $attributes) + { + return $this->isIddqd($token) ? VoterInterface::ACCESS_GRANTED : VoterInterface::ACCESS_ABSTAIN; + } + + protected function isIddqd(TokenInterface $token) + { + foreach ($token->getRoles() as $role) { + if ('ROLE_IDDQD' === $role->getRole()) { + return true; + } + } + + return false; + } + + public function supportsAttribute($attribute) + { + return true; + } + + public function supportsClass($class) + { + return true; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Twig/SecurityExtension.php b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Twig/SecurityExtension.php new file mode 100644 index 0000000..21ad8aa --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Twig/SecurityExtension.php @@ -0,0 +1,35 @@ +context = $context; + } + + public function getFunctions() + { + return array( + 'is_expr_granted' => new \Twig_Function_Method($this, 'isExprGranted', array( + 'is_safe' => true, + )), + ); + } + + public function isExprGranted($expr, $object = null) + { + return $this->context->isGranted(array(new Expression($expr)), $object); + } + + public function getName() + { + return 'jms_security_extra'; + } +} \ No newline at end of file diff --git a/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/composer.json b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/composer.json new file mode 100644 index 0000000..00e4113 --- /dev/null +++ b/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/composer.json @@ -0,0 +1,25 @@ +{ + "name": "jms/security-extra-bundle", + "description": "Enhances the Symfony2 Security Component by adding several new features", + "keywords": ["annotations","authorization"], + "type": "symfony-bundle", + "license": "Apache", + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "require": { + "symfony/framework-bundle": "2.*", + "jms/metadata": "1.1.*", + "jms/aop-bundle": "1.0.*" + }, + "recommend": { + "jms/di-extra-bundle": "1.0.*" + }, + "autoload": { + "psr-0": { "JMS\\SecurityExtraBundle": "" } + }, + "target-dir": "JMS/SecurityExtraBundle" +} diff --git a/vendor/kriswallsmith/assetic/.travis.yml b/vendor/kriswallsmith/assetic/.travis.yml new file mode 100644 index 0000000..a801b15 --- /dev/null +++ b/vendor/kriswallsmith/assetic/.travis.yml @@ -0,0 +1,16 @@ +language: php + +php: + - 5.3 + - 5.4 + +before_script: + - wget http://getcomposer.org/composer.phar + - php composer.phar install --dev + - git clone https://github.com/kamicane/packager.git vendor/packager --quiet --depth 1 + - git clone https://github.com/leafo/lessphp.git vendor/lessphp --quiet --depth 1 + - git clone https://github.com/mrclay/minify.git vendor/minify --quiet --depth 1 + - svn checkout http://cssmin.googlecode.com/svn/trunk/ vendor/cssmin --quiet + - wget --quiet -O javascript-packer.zip "http://joliclic.free.fr/php/javascript-packer/telechargement.php?id=2&action=telecharger" && mkdir -p vendor/packer && unzip -qq javascript-packer.zip -d vendor/packer; rm javascript-packer.zip + +script: phpunit --configuration phpunit.travis.xml diff --git a/vendor/kriswallsmith/assetic/CHANGELOG-1.0.md b/vendor/kriswallsmith/assetic/CHANGELOG-1.0.md new file mode 100644 index 0000000..91bb9bf --- /dev/null +++ b/vendor/kriswallsmith/assetic/CHANGELOG-1.0.md @@ -0,0 +1,29 @@ +1.0.3 (March 2, 2012) +--------------------- + + * Added "boring" option to Compass filter + * Fixed accumulation of load paths in Compass filter + * Fixed issues in CssImport and CssRewrite filters + +1.0.2 (August 26, 2011) +----------------------- + + * Twig 1.2 compatibility + * Fixed filtering of large LessCSS assets + * Fixed escaping of commands on Windows + * Misc fixes to Compass filter + * Removed default CssEmbed charset + +1.0.1 (July 15, 2011) +--------------------- + + * Fixed Twig error handling + * Removed use of STDIN + * Added inheritance of environment variables + * Fixed Compass on Windows + * Improved escaping of commands + +1.0.0 (July 10, 2011) +--------------------- + + * Initial release diff --git a/vendor/kriswallsmith/assetic/CHANGELOG-1.1.md b/vendor/kriswallsmith/assetic/CHANGELOG-1.1.md new file mode 100755 index 0000000..48ed70d --- /dev/null +++ b/vendor/kriswallsmith/assetic/CHANGELOG-1.1.md @@ -0,0 +1,9 @@ +1.1.0 (???) +--------------------- + + * Added the UglifyJsFilter + * added support for asset variables: + + Asset variables allow you to pre-compile your assets for a finite set of known + variable values, and then to simply deliver the correct asset version at runtime. + For example, this is helpful for assets with language, or browser-specific code. diff --git a/vendor/kriswallsmith/assetic/LICENSE b/vendor/kriswallsmith/assetic/LICENSE new file mode 100644 index 0000000..1060e5b --- /dev/null +++ b/vendor/kriswallsmith/assetic/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010-2012 OpenSky Project Inc + +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/vendor/kriswallsmith/assetic/README.md b/vendor/kriswallsmith/assetic/README.md new file mode 100644 index 0000000..e12e341 --- /dev/null +++ b/vendor/kriswallsmith/assetic/README.md @@ -0,0 +1,284 @@ +# Assetic ![project status](http://stillmaintained.com/kriswallsmith/assetic.png) # + +Assetic is an asset management framework for PHP. + +``` php +dump(); +``` + +Assets +------ + +An Assetic asset is something with filterable content that can be loaded and +dumped. An asset also includes metadata, some of which can be manipulated and +some of which is immutable. + +| **Property** | **Accessor** | **Mutator** | +|--------------|-----------------|---------------| +| content | getContent | setContent | +| mtime | getLastModified | n/a | +| source root | getSourceRoot | n/a | +| source path | getSourcePath | n/a | +| target path | getTargetPath | setTargetPath | + +Filters +------- + +Filters can be applied to manipulate assets. + +``` php +dump(); +``` + +The filters applied to the collection will cascade to each asset leaf if you +iterate over it. + +``` php +dump(); +} +``` + +The core provides the following filters in the `Assetic\Filter` namespace: + + * `CoffeeScriptFilter`: compiles CoffeeScript into Javascript + * `CssEmbedFilter`: embeds image data in your stylesheets + * `CssImportFilter`: inlines imported stylesheets + * `CssMinFilter`: minifies CSS + * `CssRewriteFilter`: fixes relative URLs in CSS assets when moving to a new URL + * `GoogleClosure\CompilerApiFilter`: compiles Javascript using the Google Closure Compiler API + * `GoogleClosure\CompilerJarFilter`: compiles Javascript using the Google Closure Compiler JAR + * `JpegoptimFilter`: optimize your JPEGs + * `JpegtranFilter`: optimize your JPEGs + * `LessFilter`: parses LESS into CSS (using less.js with node.js) + * `LessphpFilter`: parses LESS into CSS (using lessphp) + * `OptiPngFilter`: optimize your PNGs + * `PackerFilter`: compresses Javascript using Dean Edwards's Packer + * `PngoutFilter`: optimize your PNGs + * `CompassFilter`: Compass CSS authoring framework + * `Sass\SassFilter`: parses SASS into CSS + * `Sass\ScssFilter`: parses SCSS into CSS + * `SprocketsFilter`: Sprockets Javascript dependency management + * `StylusFilter`: parses STYL into CSS + * `Yui\CssCompressorFilter`: compresses CSS using the YUI compressor + * `Yui\JsCompressorFilter`: compresses Javascript using the YUI compressor + +Asset Manager +------------- + +An asset manager is provided for organizing assets. + +``` php +set('jquery', new FileAsset('/path/to/jquery.js')); +$am->set('base_css', new GlobAsset('/path/to/css/*')); +``` + +The asset manager can also be used to reference assets to avoid duplication. + +``` php +set('my_plugin', new AssetCollection(array( + new AssetReference($am, 'jquery'), + new FileAsset('/path/to/jquery.plugin.js'), +))); +``` + +Filter Manager +-------------- + +A filter manager is also provided for organizing filters. + +``` php +set('sass', new SassFilter('/path/to/parser/sass')); +$fm->set('yui_css', new Yui\CssCompressorFilter('/path/to/yuicompressor.jar')); +``` + +Asset Factory +------------- + +If you'd rather not create all these objects by hand, you can use the asset +factory, which will do most of the work for you. + +``` php +setAssetManager($am); +$factory->setFilterManager($fm); +$factory->setDebug(true); + +$css = $factory->createAsset(array( + '@reset', // load the asset manager's "reset" asset + 'css/src/*.scss', // load every scss files from "/path/to/asset/directory/css/src/" +), array( + 'scss', // filter through the filter manager's "scss" filter + '?yui_css', // don't use this filter in debug mode +)); + +echo $css->dump(); +``` + +Prefixing a filter name with a question mark, as `yui_css` is here, will cause +that filter to be omitted when the factory is in debug mode. + +Caching +------- + +A simple caching mechanism is provided to avoid unnecessary work. + +``` php +dump(); +$js->dump(); +$js->dump(); +``` + +Static Assets +------------- + +Alternatively you can just write filtered assets to your web directory and be +done with it. + +``` php +writeManagerAssets($am); +``` + +Twig +---- + +To use the Assetic [Twig][3] extension you must register it to your Twig +environment: + +``` php +addExtension(new AsseticExtension($factory, $debug)); +``` + +Once in place, the extension exposes a stylesheets and a javascripts tag with a syntax similar +to what the asset factory uses: + +``` html+jinja +{% stylesheets '/path/to/sass/main.sass' filter='sass,?yui_css' output='css/all.css' %} + +{% endstylesheets %} +``` + +This example will render one `link` element on the page that includes a URL +where the filtered asset can be found. + +When the extension is in debug mode, this same tag will render multiple `link` +elements, one for each asset referenced by the `css/src/*.sass` glob. The +specified filters will still be applied, unless they are marked as optional +using the `?` prefix. + +This behavior can also be triggered by setting a `debug` attribute on the tag: + +``` html+jinja +{% stylesheets 'css/*' debug=true %} ... {% stylesheets %} +``` + +These assets need to be written to the web directory so these URLs don't +return 404 errors. + +``` php +setLoader('twig', new TwigFormulaLoader($twig)); + +// loop through all your templates +foreach ($templates as $template) { + $resource = new TwigResource($twigLoader, $template); + $am->addResource($resource, 'twig'); +} + +$writer = new AssetWriter('/path/to/web'); +$writer->writeManagerAssets($am); +``` + +--- + +Assetic is based on the Python [webassets][1] library (available on +[GitHub][2]). + +[1]: http://elsdoerfer.name/docs/webassets +[2]: https://github.com/miracle2k/webassets +[3]: http://twig.sensiolabs.org diff --git a/vendor/kriswallsmith/assetic/composer.json b/vendor/kriswallsmith/assetic/composer.json new file mode 100644 index 0000000..a871c3e --- /dev/null +++ b/vendor/kriswallsmith/assetic/composer.json @@ -0,0 +1,35 @@ +{ + "name": "kriswallsmith/assetic", + "description": "Asset Management for PHP", + "keywords": ["assets", "compression", "minification"], + "homepage": "https://github.com/kriswallsmith/assetic", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "require": { + "php": ">=5.3.0", + "symfony/process": "2.1.*" + }, + "require-dev": { + "twig/twig": ">=1.6.0,<2.0", + "leafo/lessphp": "*" + }, + "suggest": { + "twig/twig": "Assetic provides the integration with the Twig templating engine", + "leafo/lessphp": "Assetic provides the integration with the lessphp LESS compiler" + }, + "autoload": { + "psr-0": { "Assetic": "src/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + } +} diff --git a/vendor/kriswallsmith/assetic/docs/en/build.md b/vendor/kriswallsmith/assetic/docs/en/build.md new file mode 100644 index 0000000..d560172 --- /dev/null +++ b/vendor/kriswallsmith/assetic/docs/en/build.md @@ -0,0 +1,32 @@ +Building and Dumping Assets +--------------------------- + +The is the simplest approach to using Assetic. It involves two steps: + + 1. Create a PHP script in your web directory that uses the Assetic OOP API to + create and output an asset. + 2. Reference that file from your template. + +For example, you could create a file in your web directory at +`assets/javascripts.php` with the following code: + + use Assetic\Asset\AssetCollection; + use Assetic\Asset\FileAsset; + use Assetic\Filter\Yui\JsCompressorFilter as YuiCompressorFilter; + + $js = new AssetCollection(array( + new FileAsset(__DIR__.'/jquery.js'), + new FileAsset(__DIR__.'/application.js'), + ), array( + new YuiCompressorFilter('/path/to/yuicompressor.jar'), + )); + + header('Content-Type: application/js'); + echo $js->dump(); + +In your HTML template you would include this generated Javascript using a +simple ` + +Next: [Basic Concepts](concepts.md) diff --git a/vendor/kriswallsmith/assetic/docs/en/concepts.md b/vendor/kriswallsmith/assetic/docs/en/concepts.md new file mode 100644 index 0000000..7af7b1f --- /dev/null +++ b/vendor/kriswallsmith/assetic/docs/en/concepts.md @@ -0,0 +1,129 @@ +In order to use the Assetic OOP API you must first understand the two central +concepts of Assetic: assets and filters. + +### What is an Asset? + +As asset is an object that has content and metadata which can be loaded and +dumped. Your assets will probably fall into three categories: Javascripts, +stylesheets and images. Most assets will be loaded from files in your +filesystem, but they can also be loaded via HTTP, a database, from a string, +or virtually anything else. All that an asset has to do is fulfill Assetic's +basic asset interface. + +### What is a Filter? + +A filter is an object that acts upon an asset's content when that asset is +loaded and/or dumped. Similar to assets, a filter can do virtually anything, +as long as it implements Assetic's filter interface. + +Here is a list of some of the tools that can be applied to assets using a +filter: + + * CoffeeScript + * CssEmbed + * CssMin + * Google Closure Compiler + * jpegoptim + * jpegtran + * Less + * LessPHP + * optipng + * Packager + * pngout + * SASS + * Sprockets (version 1) + * Stylus + * YUI Compressor + +### Using Assets and Filters + +You need to start by creating an asset object. This will probably mean +instantiating a `FileAsset` instance, which takes a filesystem path as its +first argument: + + $asset = new Assetic\Asset\FileAsset('/path/to/main.css'); + +Once you have an asset you can begin adding filters to it by calling +`ensureFilter()`. For example, you can add a filter that applies the YUI +Compressor to the contents of the asset: + + $yui = new Assetic\Filter\Yui\CssCompressorFilter('/path/to/yui.jar'); + $asset->ensureFilter($yui); + +Once you've added as many filters as you'd like you can output the finished +asset to the browser: + + header('Content-Type: text/css'); + echo $asset->dump(); + +### Asset Collections + +It is a good idea to combine assets of the same type into a single file to +avoid unnecessary HTTP requests. You can do this in Assetic using the +`AssetCollection` class. This class is just like any other asset in Assetic's +eyes as it implements the asset interface, but under the hood it allows you to +combine multiple assets into one. + + use Assetic\Asset\AssetCollection; + + $asset = new AssetCollection(array( + new FileAsset('/path/to/js/jquery.js'), + new FileAsset('/path/to/js/jquery.plugin.js'), + new FileAsset('/path/to/js/application.js'), + )); + +### Nested Asset Collections + +The collection class implements the asset interface and all assets passed into +a collection must implement the same interface, which means you can easily +nest collections within one another: + + use Assetic\Asset\AssetCollection; + use Assetic\Asset\GlobAsset; + use Assetic\Asset\HttpAsset; + + $asset = new AssetCollection(array( + new HttpAsset('http://example.com/jquery.min.js'), + new GlobAsset('/path/to/js/*'), + )); + +The `HttpAsset` class is a special asset class that loads a file over HTTP; +`GlobAsset` is a special asset collection class that loads files based on a +filesystem glob -- both implement the asset interface. + +This concept of nesting asset collection become even more powerful when you +start applying different sets of filters to each collection. Imagine some of +your application's stylesheets are written in SASS, while some are written in +vanilla CSS. You can combine all of these into one seamless CSS asset: + + use Assetic\Asset\AssetCollection; + use Assetic\Asset\GlobAsset; + use Assetic\Filter\SassFilter; + use Assetic\Filter\Yui\CssCompressorFilter; + + $css = new AssetCollection(array( + new GlobAsset('/path/to/sass/*.sass', array(new SassFilter())), + new GlobAsset('/path/to/css/*.css'), + ), array( + new YuiCompressorFilter('/path/to/yuicompressor.jar'), + )); + +You'll notice I've also applied the YUI compressor filter to the combined +asset so all CSS will be minified. + +### Iterating over an Asset Collection + +Once you have an asset collection you can iterate over it like you would a +plain old PHP array: + + echo "Source paths:\n"; + foreach ($collection as $asset) { + echo ' - '.$asset->getSourcePath()."\n"; + } + +The asset collection iterates recursively, which means you will only see the +"leaf" assets during iteration. Iteration also includes a smart filter which +ensures you only see each asset once, even if the same asset has been included +multiple times. + +Next: [Defining Assets "On The Fly"](define.md) diff --git a/vendor/kriswallsmith/assetic/docs/en/define.md b/vendor/kriswallsmith/assetic/docs/en/define.md new file mode 100644 index 0000000..3d8d3b1 --- /dev/null +++ b/vendor/kriswallsmith/assetic/docs/en/define.md @@ -0,0 +1,145 @@ +Defining Assets "On The Fly" +---------------------------- + +The second approach to using Assetic involves defining your application's +assets "on the fly" in your templates, instead of in an isolated PHP file. +Using this approach, your PHP template would look something like this: + + + +This call to `assetic_javascripts()` serves a dual purpose. It will be read by +the Assetic "formula loader" which will extract an asset "formula" that can be +used to build, dump and output the asset. It will also be executed when the +template is rendered, at which time the path to the output asset is output. + +Assetic includes the following templating helper functions: + + * `assetic_image()` + * `assetic_javascripts()` + * `assetic_stylesheets()` + +Defining assets on the fly is a much more sophisticated technique and +therefore relies on services to do the heavy lifting. The main one being the +asset factory. + +### Asset Factory + +The asset factory knows how to create asset objects using only arrays and +scalar values as input. This is the same string syntax used by the `assetic_*` +template helper functions. + + use Assetic\Factory\AssetFactory; + + $factory = new AssetFactory('/path/to/web'); + $js = $factory->createAsset(array( + 'js/jquery.js', + 'js/jquery.plugin.js', + 'js/application.js', + )); + +### Filter Manager + +You can also apply filters to asset created by the factory. To do this you +must setup a `FilterManager`, which organizes filters by a name. + + use Assetic\FilterManager; + use Assetic\Filter\GoogleClosure\ApiFilter as ClosureFilter; + + $fm = new FilterManager(); + $fm->set('closure', new ClosureFilter()); + $factory->setFilterManager($fm); + + $js = $factory->createAsset('js/*', 'closure'); + +This code creates an instance of the Google Closure Compiler filter and +assigns it the name `closure` using a filter manager. This filter manager is +then injected into the asset factory, making the filter available as `closure` +when creating assets. + +### Debug Mode + +The asset factory also introduces the concept of a debug mode. This mode +allows you to omit certain filters from assets the factory creates depending +on whether it is enabled or not. + +For example, the YUI Compressor is awesome, but it is only appropriate in a +production environment as it is very difficult to debug minified Javascript. + + use Asset\Factory\AssetFactory; + + $factory = new AssetFactory('/path/to/web', true); // debug mode is on + $factory->setFilterManager($fm); + $js = $factory->createAsset('js/*', '?closure'); + +By prefixing the `closure` filter's name with a question mark, we are telling +the factory this filter is optional and should only be applied with debug mode +is off. + +### Asset Manager and Asset References + +The asset factory provides another special string syntax that allows you to +reference assets you defined elsewhere. These are called "asset references" +and involve an asset manager which, similar to the filter manager, organizes +assets by name. + + use Assetic\AssetManager; + use Assetic\Asset\FileAsset; + use Assetic\Factory\AssetFactory; + + $am = new AssetManager(); + $am->set('jquery', new FileAsset('/path/to/jquery.js')); + + $factory = new AssetFactory('/path/to/web'); + $factory->setAssetManager($am); + + $js = $factory->createAsset(array( + '@jquery', + 'js/application.js', + )); + +### Extracting Assets from Templates + +Once you've defined a set of assets in your templates you must use the +"formula loader" service to extract these asset definitions. + + use Assetic\Factory\Loader\FunctionCallsFormulaLoader; + use Assetic\Factory\Resource\FileResource; + + $loader = new FunctionCallsFormulaLoader($factory); + $formulae = $loader->load(new FileResource('/path/to/template.php')); + +These asset formulae aren't much use by themselves. They each include just +enough information for the asset factory to create the intended asset object. +In order for these to be useful they must be wrapped in the special +`LazyAssetManager`. + +### The Lazy Asset Manager + +This service is a composition of the asset factory and one or more formula +loaders. It acts as the glue between these services behind the scenes, but can +be used just like a normal asset manager on the surface. + + use Assetic\Asset\FileAsset; + use Assetic\Factory\LazyAssetManager; + use Assetic\Factory\Loader\FunctionCallsFormulaLoader; + use Assetic\Factory\Resource\DirectoryResource; + + $am = new LazyAssetManager($factory); + $am->set('jquery', new FileAsset('/path/to/jquery.js')); + $am->setLoader('php', new FunctionCallsFormulaLoader($factory)); + $am->addResource(new DirectoryResource('/path/to/templates', '/\.php$/'), 'php'); + +### Asset Writer + +Finally, once you've create an asset manager that knows about every asset +you've defined in your templates, you must use an asset writer to actually +create the files your templates are going to be referencing. + + use Assetic\AssetWriter; + + $writer = new AssetWriter('/path/to/web'); + $writer->writeManagerAssets($am); + +After running this script, all of the assets in your asset manager will be +loaded into memory, filtered with their configured filters and dumped to your +web directory as static files, ready to be served. diff --git a/vendor/kriswallsmith/assetic/docs/en/index.md b/vendor/kriswallsmith/assetic/docs/en/index.md new file mode 100644 index 0000000..46dc590 --- /dev/null +++ b/vendor/kriswallsmith/assetic/docs/en/index.md @@ -0,0 +1,7 @@ +Table Of Contents +----------------- + + 1. [Introduction](introduction.md) + 2. [Building and Dumping Assets](build.md) + 3. [Basic Concepts](concepts.md) + 4. [Defining Assets "On The Fly"](define.md) diff --git a/vendor/kriswallsmith/assetic/docs/en/introduction.md b/vendor/kriswallsmith/assetic/docs/en/introduction.md new file mode 100644 index 0000000..352f496 --- /dev/null +++ b/vendor/kriswallsmith/assetic/docs/en/introduction.md @@ -0,0 +1,21 @@ +What is Assetic? +---------------- + +Assetic is an asset management framework for PHP 5.3. Assetic enables you to +use a variety of third party tools that will help bring order to your +application's Javascripts, stylesheets and images. + +How Do I Use Assetic? +--------------------- + +There are two distinct approaches you can take when using Assetic: + + 1. Build, dump and output assets in PHP files that you reference directly + from your templates + 2. Defining assets in your templates ("on the fly") and use a loader to + extract, dump and output them + +The first approach is simpler, but the second, with all its moving parts, +offers more flexibility and opportunity for optimization. + +Next: [Building and Dumping Assets](build.md) diff --git a/vendor/kriswallsmith/assetic/docs/ja/build.md b/vendor/kriswallsmith/assetic/docs/ja/build.md new file mode 100644 index 0000000..bee59ba --- /dev/null +++ b/vendor/kriswallsmith/assetic/docs/ja/build.md @@ -0,0 +1,30 @@ +アセットのビルドとダンプ +--------------------------- + +Asseticを使う一番単純な方法は、次の2ステップからなります。 + + 1. 公開領域内にPHPスクリプトを作成し、Assetic OOP APIを使用してアセットの作成・出力を行う + 2. テンプレートから上記のファイルを参照する + +例えば、公開領域内に`assets/javascripts.php`ファイルを作成し、 +下記のようなコードを記述します。 + + use Assetic\Asset\AssetCollection; + use Assetic\Asset\FileAsset; + use Assetic\Filter\Yui\JsCompressorFilter as YuiCompressorFilter; + + $js = new AssetCollection(array( + new FileAsset(__DIR__.'/jquery.js'), + new FileAsset(__DIR__.'/application.js'), + ), array( + new YuiCompressorFilter('/path/to/yuicompressor.jar'), + )); + + header('Content-Type: application/js'); + echo $js->dump(); + +HTMLテンプレート側では、単に` + +Next: [コンセプト](concepts.md) diff --git a/vendor/kriswallsmith/assetic/docs/ja/concepts.md b/vendor/kriswallsmith/assetic/docs/ja/concepts.md new file mode 100644 index 0000000..3479b20 --- /dev/null +++ b/vendor/kriswallsmith/assetic/docs/ja/concepts.md @@ -0,0 +1,121 @@ +Assetic OOP APIを使用するためには、まず、[アセット」と「フィルタ」の2つの重要なコンセプトを理解する必要があります。 + +### アセット + +アセットとは、読み込み、及びダンプが可能な、コンテンツとメタデータを内包しているオブジェクトの事を指します。 +大体の場合において3つのカテゴリー、すなわち、Javascriptとスタイルシート、画像のどれかに属することになるでしょう。 +読み込みの方法としては、ファイルシステムからがほとんどですが、 +HTTPやデータベース経由でも、文字列としてでも読み込みが可能で、事実上あらゆるものが読み込み可能です。 +Asseticのアセットインターフェースを満足させさえすれば良いのです。 + + +### フィルタ + +フィルタは、アセットが読み込まれる、かつ/もしくは、ダンプされる際に、 +アセットコンテンツに対して作用するオブジェクトです。 +アセットと同様に、Asseticのフィルタインターフェースを実装することで、 +どのような作用も可能になります。 + +フィルタを用いて、アセットに適用できるツール群の一覧です。 + + * CoffeeScript + * CssEmbed + * CssMin + * Google Closure Compiler + * jpegoptim + * jpegtran + * Less + * LessPHP + * optipng + * Packager + * pngout + * SASS + * Sprockets (version 1) + * Stylus + * YUI Compressor + + +### アセットとフィルタの使用 + +まずはアセットオブジェクトを作成することから始まります。 +多くの場合は`FileAsset`をインスタンス化し、ファイルシステムのパスを第一引数に渡します。 + + $asset = new Assetic\Asset\FileAsset('/path/to/main.css'); + +アセットオブジェクトを作成したら、`ensureFilter()`を呼び、フィルタを追加します。 +例えば、アセットコンテンツにYUI Compressorを適用してみましょう。 + + $yui = new Assetic\Filter\Yui\CssCompressorFilter('/path/to/yui.jar'); + $asset->ensureFilter($yui); + +任意のフィルタを追加したら、完成したアセットをブラウザに出力してみましょう。 + + header('Content-Type: text/css'); + echo $asset->dump(); + +### アセットコレクション + +1つのファイルに同じ種類のアセットをまとめて、不要なHTTPリクエストを抑えてみるのも良いでしょう。 +Asseticでは`AsseticColletion`クラスを使用することで可能となります。 +Assetic内部的には、このクラス自体は他のアセットと同様に、アセットインターフェースを実装したものですが、 +複数のアセットを1つにまとめることが可能になります。 + + use Assetic\Asset\AssetCollection; + + $asset = new AssetCollection(array( + new FileAsset('/path/to/js/jquery.js'), + new FileAsset('/path/to/js/jquery.plugin.js'), + new FileAsset('/path/to/js/application.js'), + )); + +### ネストしたアセットコレクション + +コレクションクラス自体がアセットインターフェースを実装し、コレクション内のアセットも同様に +アセットインターフェースを実装しているので、簡単にネストすることができます。 + + use Assetic\Asset\AssetCollection; + use Assetic\Asset\GlobAsset; + use Assetic\Asset\HttpAsset; + + $asset = new AssetCollection(array( + new HttpAsset('http://example.com/jquery.min.js'), + new GlobAsset('/path/to/js/*'), + )); + +`HttpAsset`は、HTTP経由でファイルを読み込むアセットクラス。 +`GlobAsset`は、ファイルシステムのglobを基にファイル群を読み込むアセットコレクションクラス。 +両者ともにアセットインターフェースを実装しています。 + +このネストしたアセットコレクションという概念は、コレクションそれぞれに異なる +フィルタ群を適用しようとしたときに、効果を発揮します。 +例えば、スタイルシートがSAASで記述されたものと、vanilla CSSを用いて記述されたものからなる +アプリケーションを考えた場合、次のようにして、全てを1つのシームレスなCSSアセットにまとめることができます。 + + use Assetic\Asset\AssetCollection; + use Assetic\Asset\GlobAsset; + use Assetic\Filter\SassFilter; + use Assetic\Filter\Yui\CssCompressorFilter; + + $css = new AssetCollection(array( + new GlobAsset('/path/to/sass/*.sass', array(new SassFilter())), + new GlobAsset('/path/to/css/*.css'), + ), array( + new YuiCompressorFilter('/path/to/yuicompressor.jar'), + )); + +上記の例では、1つにまとめられたCSSを、さらにYUI compressorフィルタを適用することで、全体を圧縮しています。 + +### アセットコレクションのイテレーション + +アセットコレクションは、旧来のPHP配列のように、イテレートできます。 + + echo "Source paths:\n"; + foreach ($collection as $asset) { + echo ' - '.$asset->getSourcePath()."\n"; + } + +アセットコレクションのイテレーションは再帰的で、「葉」にあたるアセットの取得を行います。 +また、気の利いたフィルタを内蔵しているので、同じアセットがコレクション内に複数存在する場合でも、 +一度だけのインクルードが保証されます。 + +Next: [アセットを「オンザフライ」で定義する](define.md) diff --git a/vendor/kriswallsmith/assetic/docs/ja/define.md b/vendor/kriswallsmith/assetic/docs/ja/define.md new file mode 100644 index 0000000..3b04436 --- /dev/null +++ b/vendor/kriswallsmith/assetic/docs/ja/define.md @@ -0,0 +1,140 @@ +アセットの「オンザフライ」な定義 +---------------------------------------- + +Asseticの使用方法二つ目は、独立したPHPファイルを使用する代わりに、 +テンプレートで「オンザフライ」にアセット定義をする方法です。 +このアプローチでは、PHPテンプレートは下記のようになります。 + + + +`assetic_javascripts()`の呼び出しは2つの目的を兼ねています。 +まず、「フォーミュラローダー」により走査され、アセットの構築、ダンプ、及び出力を行うための「フォーミュラ(処方箋)」が抽出されます。 +また、テンプレートのレンダー時にも実行され、アセットの出力パスが出力されます。 + +Asseticには下記のようなヘルパー関数があります。 + + * `assetic_image()` + * `assetic_javascripts()` + * `assetic_stylesheets()` + +アセットをオンザフライに定義するということは、より高度なテクニックであり、 +そのため、重い仕事をするサービスに依存することになります。 +そのうちの重要なものがアセットファクトリです。 + +### アセットファクトリ + +アセットファクトリは、アセットオブジェクトを、配列とスカラ値のみから、 +どのように作成するのか把握しています。 +`assetic_*`ヘルパー関数で使用する記法と同様のものとなります。 + + use Assetic\Factory\AssetFactory; + + $factory = new AssetFactory('/path/to/web'); + $js = $factory->createAsset(array( + 'js/jquery.js', + 'js/jquery.plugin.js', + 'js/application.js', + )); + +### フィルタマネージャー + +ファクトリによって作成されたアセットに対しても、フィルタを適用することができます。 +そのためには、`FilterManager`を設定して、名前を定義しフィルタを構成します。 + + use Assetic\FilterManager; + use Assetic\Filter\GoogleClosure\ApiFilter as ClosureFilter; + + $fm = new FilterManager(); + $fm->set('closure', new ClosureFilter()); + $factory->setFilterManager($fm); + + $js = $factory->createAsset('js/*', 'closure'); + +上記の例では、Google Closure Compilerフィルタをインスタンス化し、 +フィルタマネージャーを通じて`closure`という名前をつけています。 +このフィルタマネージャーをアセットファクトリに渡すことで、 +アセット作成時には、`closure`という名前でフィルタを使用できるようになります。 + +### デバッグモード + +アセットファクトリは、デバッグモードというコンセプトも取り入れており、 +デバッグモードの設定により、ファクトリが作成するアセットから、 +特定のフィルタを除外することができます。 + +たとえば、YUI Compressorは大変素晴らしいのですが、圧縮されたJavascriptを +デバッグするのは大変難しく、プロダクション環境でのみの使用が適切でしょう。 + + use Asset\Factory\AssetFactory; + + $factory = new AssetFactory('/path/to/web', true); // デバッグモードON + $factory->setFilterManager($fm); + $js = $factory->createAsset('js/*', '?closure'); + +フィルタ名`closure`の前にクエスチョンマークを記述すると、ファクトリに対して、 +このフィルタはオプションであり、 +デバッグモードがOFFの時にのみ適用するように通知することができます。 + +### アセットマネージャーとアセットリファレンス + +アセットファクトリにはもう一つ特別な記法があり、別の場所で定義した +アセットを参照することができるようになります。 +これを「アセットリファレンス」と呼び、アセットマネージャーを通じて、 +フィルタマネージャーと同様の、名前によるアセットの構成が可能です。 + + use Assetic\AssetManager; + use Assetic\Asset\FileAsset; + use Assetic\Factory\AssetFactory; + + $am = new AssetManager(); + $am->set('jquery', new FileAsset('/path/to/jquery.js')); + + $factory = new AssetFactory('/path/to/web'); + $factory->setAssetManager($am); + + $js = $factory->createAsset(array( + '@jquery', + 'js/application.js', + )); + +### テンプレートからのアセット抽出 + +テンプレート内でアセット群を定義したら、「フォーミュラローダー」サービスを使用して、 +アセットの定義を抽出します。 + + use Assetic\Factory\Loader\FunctionCallsFormulaLoader; + use Assetic\Factory\Resource\FileResource; + + $loader = new FunctionCallsFormulaLoader($factory); + $formulae = $loader->load(new FileResource('/path/to/template.php')); + +これらのフォーミュラ自体は、それ自体で使途はあまりなく、 +アセットファクトリが目的のアセットオブジェクトを作成するに足る情報しか持っていません。 +`LazyAssetManager`でラップすることで有益なものとなります。 + +### レイジーなアセットマネージャー + +このサービスは、アセットファクトリと、1つ以上のフォーミュラローダーから成っており、 +裏方のサービス間のグルとして動作しますが、表面上では、通常のアセットマネージャーと同じように使用することができます。 + + use Assetic\Asset\FileAsset; + use Assetic\Factory\LazyAssetManager; + use Assetic\Factory\Loader\FunctionCallsFormulaLoader; + use Assetic\Factory\Resource\DirectoryResource; + + $am = new LazyAssetManager($factory); + $am->set('jquery', new FileAsset('/path/to/jquery.js')); + $am->setLoader('php', new FunctionCallsFormulaLoader($factory)); + $am->addResource(new DirectoryResource('/path/to/templates', '/\.php$/'), 'php'); + +### アセットライター + +作成したアセットマネージャーが、テンプレート内で定義した全てのアセットを把握したら、 +アセットライターを使用して、テンプレートが参照することになる実際のファイルを作成します。 + + use Assetic\AssetWriter; + + $writer = new AssetWriter('/path/to/web'); + $writer->writeManagerAssets($am); + +上記のスクリプトを実行すると、アセットマネージャー内のすべてのアセットがメモリに読み込まれ、 +指定したフィルタが適用された後、公開領域に静的ファイルとしてダンプされ、準備完了となります。 diff --git a/vendor/kriswallsmith/assetic/docs/ja/index.md b/vendor/kriswallsmith/assetic/docs/ja/index.md new file mode 100644 index 0000000..138280d --- /dev/null +++ b/vendor/kriswallsmith/assetic/docs/ja/index.md @@ -0,0 +1,7 @@ +目次 +----- + + 1. [イントロダクション](introduction.md) + 2. [アセットの構築とダンプ](build.md) + 3. [コンセプト](concepts.md) + 4. [アセットを「オンザフライ」で定義する](define.md) diff --git a/vendor/kriswallsmith/assetic/docs/ja/introduction.md b/vendor/kriswallsmith/assetic/docs/ja/introduction.md new file mode 100644 index 0000000..0f45a8e --- /dev/null +++ b/vendor/kriswallsmith/assetic/docs/ja/introduction.md @@ -0,0 +1,18 @@ +Asseticとは +----------------- + +Asseticは、PHP5.3用のアセット管理フレームワークです。 +Asseticを導入することで、Javascriptやスタイルシート、画像をコントロールする +様々なサードパーティー製のツールを使用できるようになります。 + +Asseticの使用方法 +--------------------- + +2つの異なるアプローチがあります。 + + 1. アセットのビルド、ダンプ、出力をPHPファイルで行い、テンプレートからそのファイルを直接参照する方法 + 2. テンプレート内でアセットを(「オンザフライ」に)定義し、抽出やダンプ、出力にローダーを使用する方法 + +前者はいくらかシンプルである一方、後者は動的で柔軟性に富み、最適化が可能となります。 + +Next: [アセットの構築とダンプ](build.md) diff --git a/vendor/kriswallsmith/assetic/phpunit.travis.xml b/vendor/kriswallsmith/assetic/phpunit.travis.xml new file mode 100644 index 0000000..3a27eea --- /dev/null +++ b/vendor/kriswallsmith/assetic/phpunit.travis.xml @@ -0,0 +1,39 @@ + + + + + + ./tests/Assetic/Test/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ./src/Assetic/ + + + diff --git a/vendor/kriswallsmith/assetic/phpunit.xml.dist b/vendor/kriswallsmith/assetic/phpunit.xml.dist new file mode 100644 index 0000000..f22113e --- /dev/null +++ b/vendor/kriswallsmith/assetic/phpunit.xml.dist @@ -0,0 +1,42 @@ + + + + + + ./tests/Assetic/Test/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ./src/Assetic/ + + + diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCache.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCache.php new file mode 100644 index 0000000..9b8a59b --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCache.php @@ -0,0 +1,168 @@ + + */ +class AssetCache implements AssetInterface +{ + private $asset; + private $cache; + + public function __construct(AssetInterface $asset, CacheInterface $cache) + { + $this->asset = $asset; + $this->cache = $cache; + } + + public function ensureFilter(FilterInterface $filter) + { + $this->asset->ensureFilter($filter); + } + + public function getFilters() + { + return $this->asset->getFilters(); + } + + public function clearFilters() + { + $this->asset->clearFilters(); + } + + public function load(FilterInterface $additionalFilter = null) + { + $cacheKey = self::getCacheKey($this->asset, $additionalFilter, 'load'); + if ($this->cache->has($cacheKey)) { + $this->asset->setContent($this->cache->get($cacheKey)); + return; + } + + $this->asset->load($additionalFilter); + $this->cache->set($cacheKey, $this->asset->getContent()); + } + + public function dump(FilterInterface $additionalFilter = null) + { + $cacheKey = self::getCacheKey($this->asset, $additionalFilter, 'dump'); + if ($this->cache->has($cacheKey)) { + return $this->cache->get($cacheKey); + } + + $content = $this->asset->dump($additionalFilter); + $this->cache->set($cacheKey, $content); + + return $content; + } + + public function getContent() + { + return $this->asset->getContent(); + } + + public function setContent($content) + { + $this->asset->setContent($content); + } + + public function getSourceRoot() + { + return $this->asset->getSourceRoot(); + } + + public function getSourcePath() + { + return $this->asset->getSourcePath(); + } + + public function getTargetPath() + { + return $this->asset->getTargetPath(); + } + + public function setTargetPath($targetPath) + { + $this->asset->setTargetPath($targetPath); + } + + public function getLastModified() + { + return $this->asset->getLastModified(); + } + + public function getVars() + { + return $this->asset->getVars(); + } + + public function setValues(array $values) + { + $this->asset->setValues($values); + } + + public function getValues() + { + return $this->asset->getValues(); + } + + /** + * Returns a cache key for the current asset. + * + * The key is composed of everything but an asset's content: + * + * * source root + * * source path + * * target url + * * last modified + * * filters + * + * @param AssetInterface $asset The asset + * @param FilterInterface $additionalFilter Any additional filter being applied + * @param string $salt Salt for the key + * + * @return string A key for identifying the current asset + */ + static private function getCacheKey(AssetInterface $asset, FilterInterface $additionalFilter = null, $salt = '') + { + if ($additionalFilter) { + $asset = clone $asset; + $asset->ensureFilter($additionalFilter); + } + + $cacheKey = $asset->getSourceRoot(); + $cacheKey .= $asset->getSourcePath(); + $cacheKey .= $asset->getTargetPath(); + $cacheKey .= $asset->getLastModified(); + + foreach ($asset->getFilters() as $filter) { + if ($filter instanceof HashableInterface) { + $cacheKey .= $filter->hash(); + } else { + $cacheKey .= serialize($filter); + } + } + + if ($values = $asset->getValues()) { + asort($values); + $cacheKey .= serialize($values); + } + + return md5($cacheKey.$salt); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollection.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollection.php new file mode 100644 index 0000000..491d8dd --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollection.php @@ -0,0 +1,217 @@ + + */ +class AssetCollection implements \IteratorAggregate, AssetCollectionInterface +{ + private $assets; + private $filters; + private $sourceRoot; + private $targetPath; + private $content; + private $clones; + private $vars; + private $values; + + /** + * Constructor. + * + * @param array $assets Assets for the current collection + * @param array $filters Filters for the current collection + * @param string $sourceRoot The root directory + */ + public function __construct($assets = array(), $filters = array(), $sourceRoot = null, array $vars = array()) + { + $this->assets = array(); + foreach ($assets as $asset) { + $this->add($asset); + } + + $this->filters = new FilterCollection($filters); + $this->sourceRoot = $sourceRoot; + $this->clones = new \SplObjectStorage(); + $this->vars = $vars; + $this->values = array(); + } + + public function all() + { + return $this->assets; + } + + public function add(AssetInterface $asset) + { + $this->assets[] = $asset; + } + + public function removeLeaf(AssetInterface $needle, $graceful = false) + { + foreach ($this->assets as $i => $asset) { + $clone = isset($this->clones[$asset]) ? $this->clones[$asset] : null; + if (in_array($needle, array($asset, $clone), true)) { + unset($this->clones[$asset], $this->assets[$i]); + return true; + } elseif ($asset instanceof AssetCollectionInterface && $asset->removeLeaf($needle, true)) { + return true; + } + } + + if ($graceful) { + return false; + } + + throw new \InvalidArgumentException('Leaf not found.'); + } + + public function replaceLeaf(AssetInterface $needle, AssetInterface $replacement, $graceful = false) + { + foreach ($this->assets as $i => $asset) { + $clone = isset($this->clones[$asset]) ? $this->clones[$asset] : null; + if (in_array($needle, array($asset, $clone), true)) { + unset($this->clones[$asset]); + $this->assets[$i] = $replacement; + return true; + } elseif ($asset instanceof AssetCollectionInterface && $asset->replaceLeaf($needle, $replacement, true)) { + return true; + } + } + + if ($graceful) { + return false; + } + + throw new \InvalidArgumentException('Leaf not found.'); + } + + public function ensureFilter(FilterInterface $filter) + { + $this->filters->ensure($filter); + } + + public function getFilters() + { + return $this->filters->all(); + } + + public function clearFilters() + { + $this->filters->clear(); + } + + public function load(FilterInterface $additionalFilter = null) + { + // loop through leaves and load each asset + $parts = array(); + foreach ($this as $asset) { + $asset->load($additionalFilter); + $parts[] = $asset->getContent(); + } + + $this->content = implode("\n", $parts); + } + + public function dump(FilterInterface $additionalFilter = null) + { + // loop through leaves and dump each asset + $parts = array(); + foreach ($this as $asset) { + $parts[] = $asset->dump($additionalFilter); + } + + return implode("\n", $parts); + } + + public function getContent() + { + return $this->content; + } + + public function setContent($content) + { + $this->content = $content; + } + + public function getSourceRoot() + { + return $this->sourceRoot; + } + + public function getSourcePath() + { + } + + public function getTargetPath() + { + return $this->targetPath; + } + + public function setTargetPath($targetPath) + { + $this->targetPath = $targetPath; + } + + /** + * Returns the highest last-modified value of all assets in the current collection. + * + * @return integer|null A UNIX timestamp + */ + public function getLastModified() + { + if (!count($this->assets)) { + return; + } + + $mapper = function (AssetInterface $asset) + { + return $asset->getLastModified(); + }; + + return max(array_map($mapper, $this->assets)); + } + + /** + * Returns an iterator for looping recursively over unique leaves. + */ + public function getIterator() + { + return new \RecursiveIteratorIterator(new AssetCollectionFilterIterator(new AssetCollectionIterator($this, $this->clones))); + } + + public function getVars() + { + return $this->vars; + } + + public function setValues(array $values) + { + $this->values = $values; + + foreach ($this as $asset) { + $asset->setValues(array_intersect_key($values, array_flip($asset->getVars()))); + } + } + + public function getValues() + { + return $this->values; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollectionInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollectionInterface.php new file mode 100644 index 0000000..710e75a --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetCollectionInterface.php @@ -0,0 +1,53 @@ + + */ +interface AssetCollectionInterface extends AssetInterface, \Traversable +{ + /** + * Returns all child assets. + * + * @return array An array of AssetInterface objects + */ + function all(); + + /** + * Adds an asset to the current collection. + * + * @param AssetInterface $asset An asset + */ + function add(AssetInterface $asset); + + /** + * Removes a leaf. + * + * @param AssetInterface $needle The leaf to remove + * + * @throws InvalidArgumentException If the asset cannot be found + */ + function removeLeaf(AssetInterface $leaf); + + /** + * Replaces an existing leaf with a new one. + * + * @param AssetInterface $needle The current asset to replace + * @param AssetInterface $replacement The new asset + * + * @throws InvalidArgumentException If the asset cannot be found + */ + function replaceLeaf(AssetInterface $needle, AssetInterface $replacement); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetInterface.php new file mode 100644 index 0000000..f324317 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetInterface.php @@ -0,0 +1,156 @@ + + */ +interface AssetInterface +{ + /** + * Ensures the current asset includes the supplied filter. + * + * @param FilterInterface $filter A filter + */ + function ensureFilter(FilterInterface $filter); + + /** + * Returns an array of filters currently applied. + * + * @return array An array of filters + */ + function getFilters(); + + /** + * Clears all filters from the current asset. + */ + function clearFilters(); + + /** + * Loads the asset into memory and applies load filters. + * + * You may provide an additional filter to apply during load. + * + * @param FilterInterface $additionalFilter An additional filter + */ + function load(FilterInterface $additionalFilter = null); + + /** + * Applies dump filters and returns the asset as a string. + * + * You may provide an additional filter to apply during dump. + * + * Dumping an asset should not change its state. + * + * If the current asset has not been loaded yet, it should be + * automatically loaded at this time. + * + * @param FilterInterface $additionalFilter An additional filter + * + * @return string The filtered content of the current asset + */ + function dump(FilterInterface $additionalFilter = null); + + /** + * Returns the loaded content of the current asset. + * + * @return string The content + */ + function getContent(); + + /** + * Sets the content of the current asset. + * + * Filters can use this method to change the content of the asset. + * + * @param string $content The asset content + */ + function setContent($content); + + /** + * Returns an absolute path or URL to the source asset's root directory. + * + * This value should be an absolute path to a directory in the filesystem, + * an absolute URL with no path, or null. + * + * For example: + * + * * '/path/to/web' + * * 'http://example.com' + * * null + * + * @return string|null The asset's root + */ + function getSourceRoot(); + + /** + * Returns the relative path for the source asset. + * + * This value can be combined with the asset's source root (if both are + * non-null) to get something compatible with file_get_contents(). + * + * For example: + * + * * 'js/main.js' + * * 'main.js' + * * null + * + * @return string|null The source asset path + */ + function getSourcePath(); + + /** + * Returns the URL for the current asset. + * + * @return string|null A web URL where the asset will be dumped + */ + function getTargetPath(); + + /** + * Sets the URL for the current asset. + * + * @param string $targetPath A web URL where the asset will be dumped + */ + function setTargetPath($targetPath); + + /** + * Returns the time the current asset was last modified. + * + * @return integer|null A UNIX timestamp + */ + function getLastModified(); + + /** + * Returns an array of variable names for this asset. + * + * @return array + */ + function getVars(); + + /** + * Sets the values for the asset's variables. + * + * @param array $values + */ + function setValues(array $values); + + /** + * Returns the current values for this asset. + * + * @return array an array of strings + */ + function getValues(); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetReference.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetReference.php new file mode 100644 index 0000000..488877a --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/AssetReference.php @@ -0,0 +1,134 @@ + + */ +class AssetReference implements AssetInterface +{ + private $am; + private $name; + private $filters = array(); + + public function __construct(AssetManager $am, $name) + { + $this->am = $am; + $this->name = $name; + } + + public function ensureFilter(FilterInterface $filter) + { + $this->filters[] = $filter; + } + + public function getFilters() + { + $this->flushFilters(); + + return $this->callAsset(__FUNCTION__); + } + + public function clearFilters() + { + $this->filters = array(); + $this->callAsset(__FUNCTION__); + } + + public function load(FilterInterface $additionalFilter = null) + { + $this->flushFilters(); + + return $this->callAsset(__FUNCTION__, array($additionalFilter)); + } + + public function dump(FilterInterface $additionalFilter = null) + { + $this->flushFilters(); + + return $this->callAsset(__FUNCTION__, array($additionalFilter)); + } + + public function getContent() + { + return $this->callAsset(__FUNCTION__); + } + + public function setContent($content) + { + $this->callAsset(__FUNCTION__, array($content)); + } + + public function getSourceRoot() + { + return $this->callAsset(__FUNCTION__); + } + + public function getSourcePath() + { + return $this->callAsset(__FUNCTION__); + } + + public function getTargetPath() + { + return $this->callAsset(__FUNCTION__); + } + + public function setTargetPath($targetPath) + { + $this->callAsset(__FUNCTION__, array($targetPath)); + } + + public function getLastModified() + { + return $this->callAsset(__FUNCTION__); + } + + public function getVars() + { + return $this->callAsset(__FUNCTION__); + } + + public function getValues() + { + return $this->callAsset(__FUNCTION__); + } + + public function setValues(array $values) + { + $this->callAsset(__FUNCTION__, array($values)); + } + + // private + + private function callAsset($method, $arguments = array()) + { + $asset = $this->am->get($this->name); + + return call_user_func_array(array($asset, $method), $arguments); + } + + private function flushFilters() + { + $asset = $this->am->get($this->name); + + while ($filter = array_shift($this->filters)) { + $asset->ensureFilter($filter); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/BaseAsset.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/BaseAsset.php new file mode 100644 index 0000000..1ce93ae --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/BaseAsset.php @@ -0,0 +1,169 @@ + + */ +abstract class BaseAsset implements AssetInterface +{ + private $filters; + private $sourceRoot; + private $sourcePath; + private $targetPath; + private $content; + private $loaded; + private $vars; + private $values; + + /** + * Constructor. + * + * @param array $filters Filters for the asset + */ + public function __construct($filters = array(), $sourceRoot = null, $sourcePath = null, array $vars = array()) + { + $this->filters = new FilterCollection($filters); + $this->sourceRoot = $sourceRoot; + $this->sourcePath = $sourcePath; + $this->vars = $vars; + $this->values = array(); + $this->loaded = false; + } + + public function __clone() + { + $this->filters = clone $this->filters; + } + + public function ensureFilter(FilterInterface $filter) + { + $this->filters->ensure($filter); + } + + public function getFilters() + { + return $this->filters->all(); + } + + public function clearFilters() + { + $this->filters->clear(); + } + + /** + * Encapsulates asset loading logic. + * + * @param string $content The asset content + * @param FilterInterface $additionalFilter An additional filter + */ + protected function doLoad($content, FilterInterface $additionalFilter = null) + { + $filter = clone $this->filters; + if ($additionalFilter) { + $filter->ensure($additionalFilter); + } + + $asset = clone $this; + $asset->setContent($content); + + $filter->filterLoad($asset); + $this->content = $asset->getContent(); + + $this->loaded = true; + } + + public function dump(FilterInterface $additionalFilter = null) + { + if (!$this->loaded) { + $this->load(); + } + + $filter = clone $this->filters; + if ($additionalFilter) { + $filter->ensure($additionalFilter); + } + + $asset = clone $this; + $filter->filterDump($asset); + + return $asset->getContent(); + } + + public function getContent() + { + return $this->content; + } + + public function setContent($content) + { + $this->content = $content; + } + + public function getSourceRoot() + { + return $this->sourceRoot; + } + + public function getSourcePath() + { + return $this->sourcePath; + } + + public function getTargetPath() + { + return $this->targetPath; + } + + public function setTargetPath($targetPath) + { + if ($this->vars) { + foreach ($this->vars as $var) { + if (false === strpos($targetPath, $var)) { + throw new \RuntimeException(sprintf('The asset target path "%s" must contain the variable "{%s}".', $targetPath, $var)); + } + } + } + + $this->targetPath = $targetPath; + } + + public function getVars() + { + return $this->vars; + } + + public function setValues(array $values) + { + foreach ($values as $var => $v) { + if (!in_array($var, $this->vars, true)) { + throw new \InvalidArgumentException(sprintf('The asset with source path "%s" has no variable named "%s".', $this->sourcePath, $var)); + } + } + + $this->values = $values; + $this->loaded = false; + } + + public function getValues() + { + return $this->values; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/FileAsset.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/FileAsset.php new file mode 100644 index 0000000..0bf171c --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/FileAsset.php @@ -0,0 +1,78 @@ + + */ +class FileAsset extends BaseAsset +{ + private $source; + + /** + * Constructor. + * + * @param string $source An absolute path + * @param array $filters An array of filters + * @param string $sourceRoot The source asset root directory + * @param string $sourcePath The source asset path + * + * @throws InvalidArgumentException If the supplied root doesn't match the source when guessing the path + */ + public function __construct($source, $filters = array(), $sourceRoot = null, $sourcePath = null, array $vars = array()) + { + if (null === $sourceRoot) { + $sourceRoot = dirname($source); + if (null === $sourcePath) { + $sourcePath = basename($source); + } + } elseif (null === $sourcePath) { + if (0 !== strpos($source, $sourceRoot)) { + throw new \InvalidArgumentException(sprintf('The source "%s" is not in the root directory "%s"', $source, $sourceRoot)); + } + + $sourcePath = substr($source, strlen($sourceRoot) + 1); + } + + $this->source = $source; + + parent::__construct($filters, $sourceRoot, $sourcePath, $vars); + } + + public function load(FilterInterface $additionalFilter = null) + { + $source = PathUtils::resolvePath($this->source, $this->getVars(), + $this->getValues()); + + if (!is_file($source)) { + throw new \RuntimeException(sprintf('The source file "%s" does not exist.', $source)); + } + + $this->doLoad(file_get_contents($source), $additionalFilter); + } + + public function getLastModified() + { + $source = PathUtils::resolvePath($this->source, $this->getVars(), + $this->getValues()); + + if (!is_file($source)) { + throw new \RuntimeException(sprintf('The source file "%s" does not exist.', $source)); + } + return filemtime($source); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/GlobAsset.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/GlobAsset.php new file mode 100644 index 0000000..4f8559c --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/GlobAsset.php @@ -0,0 +1,111 @@ + + */ +class GlobAsset extends AssetCollection +{ + private $globs; + private $initialized; + + /** + * Constructor. + * + * @param string|array $globs A single glob path or array of paths + * @param array $filters An array of filters + * @param string $root The root directory + */ + public function __construct($globs, $filters = array(), $root = null, array $vars = array()) + { + $this->globs = (array) $globs; + $this->initialized = false; + + parent::__construct(array(), $filters, $root, $vars); + } + + public function all() + { + if (!$this->initialized) { + $this->initialize(); + } + + return parent::all(); + } + + public function load(FilterInterface $additionalFilter = null) + { + if (!$this->initialized) { + $this->initialize(); + } + + parent::load($additionalFilter); + } + + public function dump(FilterInterface $additionalFilter = null) + { + if (!$this->initialized) { + $this->initialize(); + } + + return parent::dump($additionalFilter); + } + + public function getLastModified() + { + if (!$this->initialized) { + $this->initialize(); + } + + return parent::getLastModified(); + } + + public function getIterator() + { + if (!$this->initialized) { + $this->initialize(); + } + + return parent::getIterator(); + } + + public function setValues(array $values) + { + parent::setValues($values); + $this->initialized = false; + } + + /** + * Initializes the collection based on the glob(s) passed in. + */ + private function initialize() + { + foreach ($this->globs as $glob) { + $glob = PathUtils::resolvePath($glob, $this->getVars(), $this->getValues()); + + if (false !== $paths = glob($glob)) { + foreach ($paths as $path) { + $this->add(new FileAsset($path, array(), $this->getSourceRoot())); + } + } + } + + $this->initialized = true; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/HttpAsset.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/HttpAsset.php new file mode 100644 index 0000000..64f2437 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/HttpAsset.php @@ -0,0 +1,79 @@ + + */ +class HttpAsset extends BaseAsset +{ + private $sourceUrl; + private $ignoreErrors; + + /** + * Constructor. + * + * @param string $sourceUrl The source URL + * @param array $filters An array of filters + * + * @throws InvalidArgumentException If the first argument is not an URL + */ + public function __construct($sourceUrl, $filters = array(), $ignoreErrors = false, array $vars = array()) + { + if (0 === strpos($sourceUrl, '//')) { + $sourceUrl = 'http:'.$sourceUrl; + } elseif (false === strpos($sourceUrl, '://')) { + throw new \InvalidArgumentException(sprintf('"%s" is not a valid URL.', $sourceUrl)); + } + + $this->sourceUrl = $sourceUrl; + $this->ignoreErrors = $ignoreErrors; + + list($scheme, $url) = explode('://', $sourceUrl, 2); + list($host, $path) = explode('/', $url, 2); + + parent::__construct($filters, $scheme.'://'.$host, $path, $vars); + } + + public function load(FilterInterface $additionalFilter = null) + { + if (false === $content = @file_get_contents(PathUtils::resolvePath( + $this->sourceUrl, $this->getVars(), $this->getValues()))) { + if ($this->ignoreErrors) { + return; + } else { + throw new \RuntimeException(sprintf('Unable to load asset from URL "%s"', $this->sourceUrl)); + } + } + + $this->doLoad($content, $additionalFilter); + } + + public function getLastModified() + { + if (false !== @file_get_contents($this->sourceUrl, false, stream_context_create(array('http' => array('method' => 'HEAD'))))) { + foreach ($http_response_header as $header) { + if (0 === stripos($header, 'Last-Modified: ')) { + list(, $mtime) = explode(':', $header, 2); + + return strtotime(trim($mtime)); + } + } + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionFilterIterator.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionFilterIterator.php new file mode 100644 index 0000000..f9ad79b --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionFilterIterator.php @@ -0,0 +1,84 @@ + + */ +class AssetCollectionFilterIterator extends \RecursiveFilterIterator +{ + private $visited; + private $sources; + + /** + * Constructor. + * + * @param AssetCollectionIterator $iterator The inner iterator + * @param array $visited An array of visited asset objects + * @param array $sources An array of visited source strings + */ + public function __construct(AssetCollectionIterator $iterator, array $visited = array(), array $sources = array()) + { + parent::__construct($iterator); + + $this->visited = $visited; + $this->sources = $sources; + } + + /** + * Determines whether the current asset is a duplicate. + * + * De-duplication is performed based on either strict equality or by + * matching sources. + * + * @return Boolean Returns true if we have not seen this asset yet + */ + public function accept() + { + $asset = $this->getInnerIterator()->current(true); + $duplicate = false; + + // check strict equality + if (in_array($asset, $this->visited, true)) { + $duplicate = true; + } else { + $this->visited[] = $asset; + } + + // check source + $sourceRoot = $asset->getSourceRoot(); + $sourcePath = $asset->getSourcePath(); + if ($sourceRoot && $sourcePath) { + $source = $sourceRoot.'/'.$sourcePath; + if (in_array($source, $this->sources)) { + $duplicate = true; + } else { + $this->sources[] = $source; + } + } + + return !$duplicate; + } + + /** + * Passes visited objects and source URLs to the child iterator. + */ + public function getChildren() + { + return new self($this->getInnerIterator()->getChildren(), $this->visited, $this->sources); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionIterator.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionIterator.php new file mode 100644 index 0000000..09330fe --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/Iterator/AssetCollectionIterator.php @@ -0,0 +1,109 @@ + + */ +class AssetCollectionIterator implements \RecursiveIterator +{ + private $assets; + private $filters; + private $output; + private $clones; + + public function __construct(AssetCollectionInterface $coll, \SplObjectStorage $clones) + { + $this->assets = $coll->all(); + $this->filters = $coll->getFilters(); + $this->output = $coll->getTargetPath(); + $this->clones = $clones; + + if (false === $pos = strpos($this->output, '.')) { + $this->output .= '_*'; + } else { + $this->output = substr($this->output, 0, $pos).'_*'.substr($this->output, $pos); + } + } + + /** + * Returns a copy of the current asset with filters and a target URL applied. + * + * @param Boolean $raw Returns the unmodified asset if true + */ + public function current($raw = false) + { + $asset = current($this->assets); + + if ($raw) { + return $asset; + } + + // clone once + if (!isset($this->clones[$asset])) { + $clone = $this->clones[$asset] = clone $asset; + + // generate a target path based on asset name + $name = sprintf('%s_%d', pathinfo($asset->getSourcePath(), PATHINFO_FILENAME) ?: 'part', $this->key() + 1); + $clone->setTargetPath(str_replace('*', $name, $this->output)); + } else { + $clone = $this->clones[$asset]; + } + + // cascade filters + foreach ($this->filters as $filter) { + $clone->ensureFilter($filter); + } + + return $clone; + } + + public function key() + { + return key($this->assets); + } + + public function next() + { + return next($this->assets); + } + + public function rewind() + { + return reset($this->assets); + } + + public function valid() + { + return false !== current($this->assets); + } + + public function hasChildren() + { + return current($this->assets) instanceof AssetCollectionInterface; + } + + /** + * @uses current() + */ + public function getChildren() + { + return new self($this->current(), $this->clones); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Asset/StringAsset.php b/vendor/kriswallsmith/assetic/src/Assetic/Asset/StringAsset.php new file mode 100644 index 0000000..6d6dc5f --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Asset/StringAsset.php @@ -0,0 +1,55 @@ + + */ +class StringAsset extends BaseAsset +{ + private $content; + private $lastModified; + + /** + * Constructor. + * + * @param string $content The content of the asset + * @param array $filters Filters for the asset + * @param string $sourceRoot The source asset root directory + * @param string $sourcePath The source asset path + */ + public function __construct($content, $filters = array(), $sourceRoot = null, $sourcePath = null) + { + $this->content = $content; + + parent::__construct($filters, $sourceRoot, $sourcePath); + } + + public function load(FilterInterface $additionalFilter = null) + { + $this->doLoad($this->content, $additionalFilter); + } + + public function setLastModified($lastModified) + { + $this->lastModified = $lastModified; + } + + public function getLastModified() + { + return $this->lastModified; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/AssetManager.php b/vendor/kriswallsmith/assetic/src/Assetic/AssetManager.php new file mode 100644 index 0000000..230e4fc --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/AssetManager.php @@ -0,0 +1,79 @@ + + */ +class AssetManager +{ + private $assets = array(); + + /** + * Gets an asset by name. + * + * @param string $name The asset name + * + * @return AssetInterface The asset + * + * @throws InvalidArgumentException If there is no asset by that name + */ + public function get($name) + { + if (!isset($this->assets[$name])) { + throw new \InvalidArgumentException(sprintf('There is no "%s" asset.', $name)); + } + + return $this->assets[$name]; + } + + /** + * Checks if the current asset manager has a certain asset. + * + * @param string $name an asset name + * + * @return Boolean True if the asset has been set, false if not + */ + public function has($name) + { + return isset($this->assets[$name]); + } + + /** + * Registers an asset to the current asset manager. + * + * @param string $name The asset name + * @param AssetInterface $asset The asset + */ + public function set($name, AssetInterface $asset) + { + if (!ctype_alnum(str_replace('_', '', $name))) { + throw new \InvalidArgumentException(sprintf('The name "%s" is invalid.', $name)); + } + + $this->assets[$name] = $asset; + } + + /** + * Returns an array of asset names. + * + * @return array An array of asset names + */ + public function getNames() + { + return array_keys($this->assets); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/AssetWriter.php b/vendor/kriswallsmith/assetic/src/Assetic/AssetWriter.php new file mode 100644 index 0000000..75f1ae5 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/AssetWriter.php @@ -0,0 +1,107 @@ + + * @author Johannes M. Schmitt + */ +class AssetWriter +{ + private $dir; + private $varValues; + + /** + * Constructor. + * + * @param string $dir The base web directory + */ + public function __construct($dir, array $varValues = array()) + { + foreach ($varValues as $var => $values) { + foreach ($values as $value) { + if (!is_string($value)) { + throw new \InvalidArgumentException(sprintf('All variable values must be strings, but got %s for variable "%s".', json_encode($value), $var)); + } + } + } + + $this->dir = $dir; + $this->varValues = $varValues; + } + + public function writeManagerAssets(AssetManager $am) + { + foreach ($am->getNames() as $name) { + $this->writeAsset($am->get($name)); + } + } + + public function writeAsset(AssetInterface $asset) + { + foreach ($this->getCombinations($asset->getVars()) as $combination) { + $asset->setValues($combination); + + static::write($this->dir.'/'.PathUtils::resolvePath( + $asset->getTargetPath(), $asset->getVars(), $asset->getValues()), + $asset->dump()); + } + } + + private function getCombinations(array $vars) + { + if (!$vars) { + return array(array()); + } + + $combinations = array(); + $nbValues = array(); + foreach ($this->varValues as $var => $values) { + if (!in_array($var, $vars, true)) { + continue; + } + + $nbValues[$var] = count($values); + } + + for ($i=array_product($nbValues),$c=$i*2; $i<$c; $i++) { + $k = $i; + $combination = array(); + + foreach ($vars as $var) { + $combination[$var] = $this->varValues[$var][$k % $nbValues[$var]]; + $k = intval($k/$nbValues[$var]); + } + + $combinations[] = $combination; + } + + return $combinations; + } + + static protected function write($path, $contents) + { + if (!is_dir($dir = dirname($path)) && false === @mkdir($dir, 0777, true)) { + throw new \RuntimeException('Unable to create directory '.$dir); + } + + if (false === @file_put_contents($path, $contents)) { + throw new \RuntimeException('Unable to write file '.$path); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Cache/ApcCache.php b/vendor/kriswallsmith/assetic/src/Assetic/Cache/ApcCache.php new file mode 100644 index 0000000..2285588 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Cache/ApcCache.php @@ -0,0 +1,66 @@ + + */ +class ApcCache implements CacheInterface +{ + public $ttl = 0; + + /** + * @see CacheInterface::has() + */ + public function has($key) + { + return apc_exists($key); + } + + /** + * @see CacheInterface::get() + */ + public function get($key) + { + $value = apc_fetch($key, $success); + + if (!$success) { + throw new \RuntimeException('There is no cached value for ' . $key); + } + + return $value; + } + + /** + * @see CacheInterface::set() + */ + public function set($key, $value) + { + $store = apc_store($key, $value, $this->ttl); + + if (!$store) { + throw new \RuntimeException('Unable to store "' . $key . '" for ' . $this->ttl . ' seconds.'); + } + + return $store; + } + + /** + * @see CacheInterface::remove() + */ + public function remove($key) + { + return apc_delete($key); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Cache/CacheInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Cache/CacheInterface.php new file mode 100644 index 0000000..8b9334d --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Cache/CacheInterface.php @@ -0,0 +1,53 @@ + + */ +interface CacheInterface +{ + /** + * Checks if the cache has a value for a key. + * + * @param string $key A unique key + * + * @return Boolean Whether the cache has a value for this key + */ + function has($key); + + /** + * Returns the value for a key. + * + * @param string $key A unique key + * + * @return string|null The value in the cache + */ + function get($key); + + /** + * Sets a value in the cache. + * + * @param string $key A unique key + * @param string $value The value to cache + */ + function set($key, $value); + + /** + * Removes a value from the cache. + * + * @param string $key A unique key + */ + function remove($key); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Cache/ConfigCache.php b/vendor/kriswallsmith/assetic/src/Assetic/Cache/ConfigCache.php new file mode 100644 index 0000000..de60c9e --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Cache/ConfigCache.php @@ -0,0 +1,123 @@ + + */ +class ConfigCache +{ + private $dir; + + /** + * Construct. + * + * @param string $dir The cache directory + */ + public function __construct($dir) + { + $this->dir = $dir; + } + + /** + * Checks of the cache has a file. + * + * @param string $resource A cache key + * + * @return Boolean True if a file exists + */ + public function has($resource) + { + return file_exists($this->getSourcePath($resource)); + } + + /** + * Writes a value to a file. + * + * @param string $resource A cache key + * @param mixed $value A value to cache + */ + public function set($resource, $value) + { + $path = $this->getSourcePath($resource); + + if (!is_dir($dir = dirname($path)) && false === @mkdir($dir, 0777, true)) { + // @codeCoverageIgnoreStart + throw new \RuntimeException('Unable to create directory '.$dir); + // @codeCoverageIgnoreEnd + } + + if (false === @file_put_contents($path, sprintf("getSourcePath($resource); + + if (!file_exists($path)) { + throw new \RuntimeException('There is no cached value for '.$resource); + } + + return include $path; + } + + /** + * Returns a timestamp for when the cache was created. + * + * @param string $resource A cache key + * + * @return integer A UNIX timestamp + */ + public function getTimestamp($resource) + { + $path = $this->getSourcePath($resource); + + if (!file_exists($path)) { + throw new \RuntimeException('There is no cached value for '.$resource); + } + + if (false === $mtime = @filemtime($path)) { + // @codeCoverageIgnoreStart + throw new \RuntimeException('Unable to determine file mtime for '.$path); + // @codeCoverageIgnoreEnd + } + + return $mtime; + } + + /** + * Returns the path where the file corresponding to the supplied cache key can be included from. + * + * @param string $resource A cache key + * + * @return string A file path + */ + private function getSourcePath($resource) + { + $key = md5($resource); + + return $this->dir.'/'.$key[0].'/'.$key.'.php'; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Cache/ExpiringCache.php b/vendor/kriswallsmith/assetic/src/Assetic/Cache/ExpiringCache.php new file mode 100644 index 0000000..9e95b6d --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Cache/ExpiringCache.php @@ -0,0 +1,60 @@ + + */ +class ExpiringCache implements CacheInterface +{ + private $cache; + private $lifetime; + + public function __construct(CacheInterface $cache, $lifetime) + { + $this->cache = $cache; + $this->lifetime = $lifetime; + } + + public function has($key) + { + if ($this->cache->has($key)) { + if (time() < $this->cache->get($key.'.expires')) { + return true; + } + + $this->cache->remove($key.'.expires'); + $this->cache->remove($key); + } + + return false; + } + + public function get($key) + { + return $this->cache->get($key); + } + + public function set($key, $value) + { + $this->cache->set($key.'.expires', time() + $this->lifetime); + $this->cache->set($key, $value); + } + + public function remove($key) + { + $this->cache->remove($key.'.expires'); + $this->cache->remove($key); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Cache/FilesystemCache.php b/vendor/kriswallsmith/assetic/src/Assetic/Cache/FilesystemCache.php new file mode 100644 index 0000000..45cfbdb --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Cache/FilesystemCache.php @@ -0,0 +1,65 @@ + + */ +class FilesystemCache implements CacheInterface +{ + private $dir; + + public function __construct($dir) + { + $this->dir = $dir; + } + + public function has($key) + { + return file_exists($this->dir.'/'.$key); + } + + public function get($key) + { + $path = $this->dir.'/'.$key; + + if (!file_exists($path)) { + throw new \RuntimeException('There is no cached value for '.$key); + } + + return file_get_contents($path); + } + + public function set($key, $value) + { + if (!is_dir($this->dir) && false === @mkdir($this->dir, 0777, true)) { + throw new \RuntimeException('Unable to create directory '.$this->dir); + } + + $path = $this->dir.'/'.$key; + + if (false === @file_put_contents($path, $value)) { + throw new \RuntimeException('Unable to write file '.$path); + } + } + + public function remove($key) + { + $path = $this->dir.'/'.$key; + + if (file_exists($path) && false === @unlink($path)) { + throw new \RuntimeException('Unable to remove file '.$path); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Exception/Exception.php b/vendor/kriswallsmith/assetic/src/Assetic/Exception/Exception.php new file mode 100644 index 0000000..1d1e928 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Exception/Exception.php @@ -0,0 +1,21 @@ + + */ +interface Exception +{ +} \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Exception/FilterException.php b/vendor/kriswallsmith/assetic/src/Assetic/Exception/FilterException.php new file mode 100644 index 0000000..82203de --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Exception/FilterException.php @@ -0,0 +1,73 @@ + + */ +class FilterException extends \RuntimeException implements Exception +{ + private $originalMessage; + private $input; + + public static function fromProcess(Process $proc) + { + $message = sprintf("An error occurred while running:\n%s", $proc->getCommandLine()); + + $errorOutput = $proc->getErrorOutput(); + if (!empty($errorOutput)) { + $message .= "\n\nError Output:\n".str_replace("\r", '', $errorOutput); + } + + $output = $proc->getOutput(); + if (!empty($output)) { + $message .= "\n\nOutput:\n".str_replace("\r", '', $output); + } + + return new self($message); + } + + public function __construct($message, $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->originalMessage = $message; + } + + public function setInput($input) + { + $this->input = $input; + $this->updateMessage(); + + return $this; + } + + public function getInput() + { + return $this->input; + } + + private function updateMessage() + { + $message = $this->originalMessage; + + if (!empty($this->input)) { + $message .= "\n\nInput:\n".$this->input; + } + + $this->message = $message; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticExtension.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticExtension.php new file mode 100644 index 0000000..6d91443 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticExtension.php @@ -0,0 +1,76 @@ +factory = $factory; + $this->functions = array(); + $this->valueSupplier = $valueSupplier; + + foreach ($functions as $function => $options) { + if (is_integer($function) && is_string($options)) { + $this->functions[$options] = array('filter' => $options); + } else { + $this->functions[$function] = $options + array('filter' => $function); + } + } + } + + public function getTokenParsers() + { + return array( + new AsseticTokenParser($this->factory, 'javascripts', 'js/*.js'), + new AsseticTokenParser($this->factory, 'stylesheets', 'css/*.css'), + new AsseticTokenParser($this->factory, 'image', 'images/*', true), + ); + } + + public function getFunctions() + { + $functions = array(); + foreach ($this->functions as $function => $filter) { + $functions[$function] = new AsseticFilterFunction($function); + } + + return $functions; + } + + public function getGlobals() + { + return array( + 'assetic' => array( + 'debug' => $this->factory->isDebug(), + 'vars' => null !== $this->valueSupplier ? $this->valueSupplier->getValues() : array(), + ), + ); + } + + public function getFilterInvoker($function) + { + return new AsseticFilterInvoker($this->factory, $this->functions[$function]); + } + + public function getName() + { + return 'assetic'; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterFunction.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterFunction.php new file mode 100644 index 0000000..c5c79a8 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterFunction.php @@ -0,0 +1,29 @@ +filter = $filter; + + parent::__construct($options); + } + + public function compile() + { + return sprintf('$this->env->getExtension(\'assetic\')->getFilterInvoker(\'%s\')->invoke', $this->filter); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterInvoker.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterInvoker.php new file mode 100644 index 0000000..78accd5 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticFilterInvoker.php @@ -0,0 +1,61 @@ + + */ +class AsseticFilterInvoker +{ + private $factory; + private $filters; + private $options; + + public function __construct($factory, $filter) + { + $this->factory = $factory; + + if (is_array($filter) && isset($filter['filter'])) { + $this->filters = (array) $filter['filter']; + $this->options = isset($filter['options']) ? (array) $filter['options'] : array(); + } else { + $this->filters = (array) $filter; + $this->options = array(); + } + } + + public function getFactory() + { + return $this->factory; + } + + public function getFilters() + { + return $this->filters; + } + + public function getOptions() + { + return $this->options; + } + + public function invoke($input, array $options = array()) + { + $asset = $this->factory->createAsset($input, $this->filters, $options + $this->options); + + return $asset->getTargetPath(); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticNode.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticNode.php new file mode 100644 index 0000000..c60daee --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticNode.php @@ -0,0 +1,166 @@ + $body); + + $attributes = array_replace( + array('debug' => null, 'combine' => null, 'var_name' => 'asset_url'), + $attributes, + array('asset' => $asset, 'inputs' => $inputs, 'filters' => $filters, 'name' => $name) + ); + + parent::__construct($nodes, $attributes, $lineno, $tag); + } + + public function compile(\Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + + $combine = $this->getAttribute('combine'); + $debug = $this->getAttribute('debug'); + + if (null === $combine && null !== $debug) { + $combine = !$debug; + } + + if (null === $combine) { + $compiler + ->write("if (isset(\$context['assetic']['debug']) && \$context['assetic']['debug']) {\n") + ->indent() + ; + + $this->compileDebug($compiler); + + $compiler + ->outdent() + ->write("} else {\n") + ->indent() + ; + + $this->compileAsset($compiler, $this->getAttribute('asset'), $this->getAttribute('name')); + + $compiler + ->outdent() + ->write("}\n") + ; + } elseif ($combine) { + $this->compileAsset($compiler, $this->getAttribute('asset'), $this->getAttribute('name')); + } else { + $this->compileDebug($compiler); + } + + $compiler + ->write('unset($context[') + ->repr($this->getAttribute('var_name')) + ->raw("]);\n") + ; + } + + protected function compileDebug(\Twig_Compiler $compiler) + { + $i = 0; + foreach ($this->getAttribute('asset') as $leaf) { + $leafName = $this->getAttribute('name').'_'.$i++; + $this->compileAsset($compiler, $leaf, $leafName); + } + } + + protected function compileAsset(\Twig_Compiler $compiler, AssetInterface $asset, $name) + { + if ($vars = $asset->getVars()) { + $compiler->write("// check variable conditions\n"); + + foreach ($vars as $var) { + $compiler + ->write("if (!isset(\$context['assetic']['vars']['$var'])) {\n") + ->indent() + ->write("throw new \RuntimeException(sprintf('The asset \"".$name."\" expected variable \"".$var."\" to be set, but got only these vars: %s. Did you set-up a value supplier?', isset(\$context['assetic']['vars']) && \$context['assetic']['vars'] ? implode(', ', \$context['assetic']['vars']) : '# none #'));\n") + ->outdent() + ->write("}\n") + ; + } + + $compiler->raw("\n"); + } + + $compiler + ->write("// asset \"$name\"\n") + ->write('$context[') + ->repr($this->getAttribute('var_name')) + ->raw('] = ') + ; + + $this->compileAssetUrl($compiler, $asset, $name); + + $compiler + ->raw(";\n") + ->subcompile($this->getNode('body')) + ; + } + + protected function compileAssetUrl(\Twig_Compiler $compiler, AssetInterface $asset, $name) + { + if (!$vars = $asset->getVars()) { + $compiler->repr($asset->getTargetPath()); + + return; + } + + $compiler + ->raw("strtr(") + ->string($asset->getTargetPath()) + ->raw(", array("); + ; + + $first = true; + foreach ($vars as $var) { + if (!$first) { + $compiler->raw(", "); + } + $first = false; + + $compiler + ->string("{".$var."}") + ->raw(" => \$context['assetic']['vars']['$var']") + ; + } + + $compiler + ->raw("))") + ; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticTokenParser.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticTokenParser.php new file mode 100644 index 0000000..c63fd06 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/AsseticTokenParser.php @@ -0,0 +1,150 @@ +factory = $factory; + $this->tag = $tag; + $this->output = $output; + $this->single = $single; + $this->extensions = $extensions; + } + + public function parse(\Twig_Token $token) + { + $inputs = array(); + $filters = array(); + $name = null; + $attributes = array( + 'output' => $this->output, + 'var_name' => 'asset_url', + 'vars' => array(), + ); + + $stream = $this->parser->getStream(); + while (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) { + if ($stream->test(\Twig_Token::STRING_TYPE)) { + // '@jquery', 'js/src/core/*', 'js/src/extra.js' + $inputs[] = $stream->next()->getValue(); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'filter')) { + // filter='yui_js' + $stream->next(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $filters = array_merge($filters, array_filter(array_map('trim', explode(',', $stream->expect(\Twig_Token::STRING_TYPE)->getValue())))); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'output')) { + // output='js/packed/*.js' OR output='js/core.js' + $stream->next(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $attributes['output'] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue(); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'name')) { + // name='core_js' + $stream->next(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $name = $stream->expect(\Twig_Token::STRING_TYPE)->getValue(); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'as')) { + // as='the_url' + $stream->next(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $attributes['var_name'] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue(); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'debug')) { + // debug=true + $stream->next(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $attributes['debug'] = 'true' == $stream->expect(\Twig_Token::NAME_TYPE, array('true', 'false'))->getValue(); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'combine')) { + // combine=true + $stream->next(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $attributes['combine'] = 'true' == $stream->expect(\Twig_Token::NAME_TYPE, array('true', 'false'))->getValue(); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, 'vars')) { + // vars=['locale','browser'] + $stream->next(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $stream->expect(\Twig_Token::PUNCTUATION_TYPE, '['); + + while ($stream->test(\Twig_Token::STRING_TYPE)) { + $attributes['vars'][] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue(); + + if (!$stream->test(\Twig_Token::PUNCTUATION_TYPE, ',')) { + break; + } + + $stream->next(); + } + + $stream->expect(\Twig_Token::PUNCTUATION_TYPE, ']'); + } elseif ($stream->test(\Twig_Token::NAME_TYPE, $this->extensions)) { + // an arbitrary configured attribute + $key = $stream->next()->getValue(); + $stream->expect(\Twig_Token::OPERATOR_TYPE, '='); + $attributes[$key] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue(); + } else { + $token = $stream->getCurrent(); + throw new \Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', \Twig_Token::typeToEnglish($token->getType(), $token->getLine()), $token->getValue()), $token->getLine()); + } + } + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + $endtag = 'end'.$this->getTag(); + $test = function(\Twig_Token $token) use($endtag) { return $token->test($endtag); }; + $body = $this->parser->subparse($test, true); + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + if ($this->single && 1 < count($inputs)) { + $inputs = array_slice($inputs, -1); + } + + if (!$name) { + $name = $this->factory->generateAssetName($inputs, $filters, $attributes); + } + + $asset = $this->factory->createAsset($inputs, $filters, $attributes + array('name' => $name)); + + return $this->createNode($asset, $body, $inputs, $filters, $name, $attributes, $token->getLine(), $this->getTag()); + } + + public function getTag() + { + return $this->tag; + } + + protected function createNode(AssetInterface $asset, \Twig_NodeInterface $body, array $inputs, array $filters, $name, array $attributes = array(), $lineno = 0, $tag = null) + { + return new AsseticNode($asset, $body, $inputs, $filters, $name, $attributes, $lineno, $tag); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigFormulaLoader.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigFormulaLoader.php new file mode 100644 index 0000000..6edd25b --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigFormulaLoader.php @@ -0,0 +1,97 @@ + + */ +class TwigFormulaLoader implements FormulaLoaderInterface +{ + private $twig; + + public function __construct(\Twig_Environment $twig) + { + $this->twig = $twig; + } + + public function load(ResourceInterface $resource) + { + try { + $tokens = $this->twig->tokenize($resource->getContent(), (string) $resource); + $nodes = $this->twig->parse($tokens); + } catch (\Exception $e) { + return array(); + } + + return $this->loadNode($nodes); + } + + /** + * Loads assets from the supplied node. + * + * @return array An array of asset formulae indexed by name + */ + private function loadNode(\Twig_Node $node) + { + $formulae = array(); + + if ($node instanceof AsseticNode) { + $formulae[$node->getAttribute('name')] = array( + $node->getAttribute('inputs'), + $node->getAttribute('filters'), + array( + 'output' => $node->getAttribute('asset')->getTargetPath(), + 'name' => $node->getAttribute('name'), + 'debug' => $node->getAttribute('debug'), + 'combine' => $node->getAttribute('combine'), + 'vars' => $node->getAttribute('vars'), + ), + ); + } elseif ($node instanceof \Twig_Node_Expression_Function) { + $name = version_compare(\Twig_Environment::VERSION, '1.2.0-DEV', '<') + ? $node->getNode('name')->getAttribute('name') + : $node->getAttribute('name'); + + if ($this->twig->getFunction($name) instanceof AsseticFilterFunction) { + $arguments = array(); + foreach ($node->getNode('arguments') as $argument) { + $arguments[] = eval('return '.$this->twig->compile($argument).';'); + } + + $invoker = $this->twig->getExtension('assetic')->getFilterInvoker($name); + + $inputs = isset($arguments[0]) ? (array) $arguments[0] : array(); + $filters = $invoker->getFilters(); + $options = array_replace($invoker->getOptions(), isset($arguments[1]) ? $arguments[1] : array()); + + if (!isset($options['name'])) { + $options['name'] = $invoker->getFactory()->generateAssetName($inputs, $filters, $options); + } + + $formulae[$options['name']] = array($inputs, $filters, $options); + } + } + + foreach ($node as $child) { + if ($child instanceof \Twig_Node) { + $formulae += $this->loadNode($child); + } + } + + return $formulae; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigResource.php b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigResource.php new file mode 100644 index 0000000..58d4b45 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Extension/Twig/TwigResource.php @@ -0,0 +1,54 @@ + + */ +class TwigResource implements ResourceInterface +{ + private $loader; + private $name; + + public function __construct(\Twig_LoaderInterface $loader, $name) + { + $this->loader = $loader; + $this->name = $name; + } + + public function getContent() + { + try { + return $this->loader->getSource($this->name); + } catch (\Twig_Error_Loader $e) { + return ''; + } + } + + public function isFresh($timestamp) + { + try { + return $this->loader->isFresh($this->name, $timestamp); + } catch (\Twig_Error_Loader $e) { + return false; + } + } + + public function __toString() + { + return $this->name; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/AssetFactory.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/AssetFactory.php new file mode 100644 index 0000000..e946d86 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/AssetFactory.php @@ -0,0 +1,386 @@ + + */ +class AssetFactory +{ + private $root; + private $debug; + private $output; + private $workers; + private $am; + private $fm; + + /** + * Constructor. + * + * @param string $root The default root directory + * @param string $output The default output string + * @param Boolean $debug Filters prefixed with a "?" will be omitted in debug mode + */ + public function __construct($root, $debug = false) + { + $this->root = rtrim($root, '/'); + $this->debug = $debug; + $this->output = 'assetic/*'; + $this->workers = array(); + } + + /** + * Sets debug mode for the current factory. + * + * @param Boolean $debug Debug mode + */ + public function setDebug($debug) + { + $this->debug = $debug; + } + + /** + * Checks if the factory is in debug mode. + * + * @return Boolean Debug mode + */ + public function isDebug() + { + return $this->debug; + } + + /** + * Sets the default output string. + * + * @param string $output The default output string + */ + public function setDefaultOutput($output) + { + $this->output = $output; + } + + /** + * Adds a factory worker. + * + * @param WorkerInterface $worker A worker + */ + public function addWorker(WorkerInterface $worker) + { + $this->workers[] = $worker; + } + + /** + * Returns the current asset manager. + * + * @return AssetManager|null The asset manager + */ + public function getAssetManager() + { + return $this->am; + } + + /** + * Sets the asset manager to use when creating asset references. + * + * @param AssetManager $am The asset manager + */ + public function setAssetManager(AssetManager $am) + { + $this->am = $am; + } + + /** + * Returns the current filter manager. + * + * @return FilterManager|null The filter manager + */ + public function getFilterManager() + { + return $this->fm; + } + + /** + * Sets the filter manager to use when adding filters. + * + * @param FilterManager $fm The filter manager + */ + public function setFilterManager(FilterManager $fm) + { + $this->fm = $fm; + } + + /** + * Creates a new asset. + * + * Prefixing a filter name with a question mark will cause it to be + * omitted when the factory is in debug mode. + * + * Available options: + * + * * output: An output string + * * name: An asset name for interpolation in output patterns + * * debug: Forces debug mode on or off for this asset + * * root: An array or string of more root directories + * + * @param array|string $inputs An array of input strings + * @param array|string $filters An array of filter names + * @param array $options An array of options + * + * @return AssetCollection An asset collection + */ + public function createAsset($inputs = array(), $filters = array(), array $options = array()) + { + if (!is_array($inputs)) { + $inputs = array($inputs); + } + + if (!is_array($filters)) { + $filters = array($filters); + } + + if (!isset($options['output'])) { + $options['output'] = $this->output; + } + + if (!isset($options['vars'])) { + $options['vars'] = array(); + } + + if (!isset($options['debug'])) { + $options['debug'] = $this->debug; + } + + if (!isset($options['root'])) { + $options['root'] = array($this->root); + } else { + if (!is_array($options['root'])) { + $options['root'] = array($options['root']); + } + + $options['root'][] = $this->root; + } + + if (!isset($options['name'])) { + $options['name'] = $this->generateAssetName($inputs, $filters, $options); + } + + $asset = $this->createAssetCollection(array(), $options); + $extensions = array(); + + // inner assets + foreach ($inputs as $input) { + if (is_array($input)) { + // nested formula + $asset->add(call_user_func_array(array($this, 'createAsset'), $input)); + } else { + $asset->add($this->parseInput($input, $options)); + $extensions[pathinfo($input, PATHINFO_EXTENSION)] = true; + } + } + + // filters + foreach ($filters as $filter) { + if ('?' != $filter[0]) { + $asset->ensureFilter($this->getFilter($filter)); + } elseif (!$options['debug']) { + $asset->ensureFilter($this->getFilter(substr($filter, 1))); + } + } + + // append variables + if (!empty($options['vars'])) { + $toAdd = array(); + foreach ($options['vars'] as $var) { + if (false !== strpos($options['output'], '{'.$var.'}')) { + continue; + } + + $toAdd[] = '{'.$var.'}'; + } + + if ($toAdd) { + $options['output'] = str_replace('*', '*.'.implode('.', $toAdd), $options['output']); + } + } + + // append consensus extension if missing + if (1 == count($extensions) && !pathinfo($options['output'], PATHINFO_EXTENSION) && $extension = key($extensions)) { + $options['output'] .= '.'.$extension; + } + + // output --> target url + $asset->setTargetPath(str_replace('*', $options['name'], $options['output'])); + + // apply workers and return + return $this->applyWorkers($asset); + } + + public function generateAssetName($inputs, $filters, $options = array()) + { + foreach (array_diff(array_keys($options), array('output', 'debug', 'root')) as $key) { + unset($options[$key]); + } + + ksort($options); + + return substr(sha1(serialize($inputs).serialize($filters).serialize($options)), 0, 7); + } + + /** + * Parses an input string string into an asset. + * + * The input string can be one of the following: + * + * * A reference: If the string starts with an "at" sign it will be interpreted as a reference to an asset in the asset manager + * * An absolute URL: If the string contains "://" or starts with "//" it will be interpreted as an HTTP asset + * * A glob: If the string contains a "*" it will be interpreted as a glob + * * A path: Otherwise the string is interpreted as a filesystem path + * + * Both globs and paths will be absolutized using the current root directory. + * + * @param string $input An input string + * @param array $options An array of options + * + * @return AssetInterface An asset + */ + protected function parseInput($input, array $options = array()) + { + if ('@' == $input[0]) { + return $this->createAssetReference(substr($input, 1)); + } + + if (false !== strpos($input, '://') || 0 === strpos($input, '//')) { + return $this->createHttpAsset($input, $options['vars']); + } + + if (self::isAbsolutePath($input)) { + if ($root = self::findRootDir($input, $options['root'])) { + $path = ltrim(substr($input, strlen($root)), '/'); + } else { + $path = null; + } + } else { + $root = $this->root; + $path = $input; + $input = $this->root.'/'.$path; + } + if (false !== strpos($input, '*')) { + return $this->createGlobAsset($input, $root, $options['vars']); + } else { + return $this->createFileAsset($input, $root, $path, $options['vars']); + } + } + + protected function createAssetCollection(array $assets = array(), array $options = array()) + { + return new AssetCollection($assets, array(), null, isset($options['vars']) ? $options['vars'] : array()); + } + + protected function createAssetReference($name) + { + if (!$this->am) { + throw new \LogicException('There is no asset manager.'); + } + + return new AssetReference($this->am, $name); + } + + protected function createHttpAsset($sourceUrl, $vars) + { + return new HttpAsset($sourceUrl, array(), false, $vars); + } + + protected function createGlobAsset($glob, $root = null, $vars) + { + return new GlobAsset($glob, array(), $root, $vars); + } + + protected function createFileAsset($source, $root = null, $path = null, $vars) + { + return new FileAsset($source, array(), $root, $path, $vars); + } + + protected function getFilter($name) + { + if (!$this->fm) { + throw new \LogicException('There is no filter manager.'); + } + + return $this->fm->get($name); + } + + /** + * Filters an asset collection through the factory workers. + * + * Each leaf asset will be processed first, followed by the asset + * collection itself. + * + * @param AssetCollectionInterface $asset An asset collection + */ + private function applyWorkers(AssetCollectionInterface $asset) + { + foreach ($asset as $leaf) { + foreach ($this->workers as $worker) { + $retval = $worker->process($leaf); + + if ($retval instanceof AssetInterface && $leaf !== $retval) { + $asset->replaceLeaf($leaf, $retval); + } + } + } + + foreach ($this->workers as $worker) { + $retval = $worker->process($asset); + + if ($retval instanceof AssetInterface) { + $asset = $retval; + } + } + + return $asset instanceof AssetCollectionInterface ? $asset : $this->createAssetCollection(array($asset)); + } + + static private function isAbsolutePath($path) + { + return '/' == $path[0] || '\\' == $path[0] || (3 < strlen($path) && ctype_alpha($path[0]) && $path[1] == ':' && ('\\' == $path[2] || '/' == $path[2])); + } + + /** + * Loops through the root directories and returns the first match. + * + * @param string $path An absolute path + * @param array $roots An array of root directories + * + * @return string|null The matching root directory, if found + */ + static private function findRootDir($path, array $roots) + { + foreach ($roots as $root) { + if (0 === strpos($path, $root)) { + return $root; + } + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/LazyAssetManager.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/LazyAssetManager.php new file mode 100644 index 0000000..9a5fa13 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/LazyAssetManager.php @@ -0,0 +1,204 @@ + + */ +class LazyAssetManager extends AssetManager +{ + private $factory; + private $loaders; + private $resources; + private $formulae; + private $loaded; + private $loading; + + /** + * Constructor. + * + * @param AssetFactory $factory The asset factory + * @param array $loaders An array of loaders indexed by alias + */ + public function __construct(AssetFactory $factory, $loaders = array()) + { + $this->factory = $factory; + $this->loaders = array(); + $this->resources = array(); + $this->formulae = array(); + $this->loaded = false; + $this->loading = false; + + foreach ($loaders as $alias => $loader) { + $this->setLoader($alias, $loader); + } + } + + /** + * Adds a loader to the asset manager. + * + * @param string $alias An alias for the loader + * @param FormulaLoaderInterface $loader A loader + */ + public function setLoader($alias, FormulaLoaderInterface $loader) + { + $this->loaders[$alias] = $loader; + $this->loaded = false; + } + + /** + * Adds a resource to the asset manager. + * + * @param ResourceInterface $resource A resource + * @param string $loader The loader alias for this resource + */ + public function addResource(ResourceInterface $resource, $loader) + { + $this->resources[$loader][] = $resource; + $this->loaded = false; + } + + /** + * Returns an array of resources. + * + * @return array An array of resources + */ + public function getResources() + { + $resources = array(); + foreach ($this->resources as $r) { + $resources = array_merge($resources, $r); + } + + return $resources; + } + + /** + * Checks for an asset formula. + * + * @param string $name An asset name + * + * @return Boolean If there is a formula + */ + public function hasFormula($name) + { + if (!$this->loaded) { + $this->load(); + } + + return isset($this->formulae[$name]); + } + + /** + * Returns an asset's formula. + * + * @param string $name An asset name + * + * @return array The formula + * + * @throws InvalidArgumentException If there is no formula by that name + */ + public function getFormula($name) + { + if (!$this->loaded) { + $this->load(); + } + + if (!isset($this->formulae[$name])) { + throw new \InvalidArgumentException(sprintf('There is no "%s" formula.', $name)); + } + + return $this->formulae[$name]; + } + + /** + * Sets a formula on the asset manager. + * + * @param string $name An asset name + * @param array $formula A formula + */ + public function setFormula($name, array $formula) + { + $this->formulae[$name] = $formula; + } + + /** + * Loads formulae from resources. + * + * @throws LogicException If a resource has been added to an invalid loader + */ + public function load() + { + if ($this->loading) { + return; + } + + if ($diff = array_diff(array_keys($this->resources), array_keys($this->loaders))) { + throw new \LogicException('The following loader(s) are not registered: '.implode(', ', $diff)); + } + + $this->loading = true; + + foreach ($this->resources as $loader => $resources) { + foreach ($resources as $resource) { + $this->formulae = array_replace($this->formulae, $this->loaders[$loader]->load($resource)); + } + } + + $this->loaded = true; + $this->loading = false; + } + + public function get($name) + { + if (!$this->loaded) { + $this->load(); + } + + if (!parent::has($name) && isset($this->formulae[$name])) { + list($inputs, $filters, $options) = $this->formulae[$name]; + $options['name'] = $name; + parent::set($name, $this->factory->createAsset($inputs, $filters, $options)); + } + + return parent::get($name); + } + + public function has($name) + { + if (!$this->loaded) { + $this->load(); + } + + return isset($this->formulae[$name]) || parent::has($name); + } + + public function getNames() + { + if (!$this->loaded) { + $this->load(); + } + + return array_unique(array_merge(parent::getNames(), array_keys($this->formulae))); + } + + public function isDebug() + { + return $this->factory->isDebug(); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/BasePhpFormulaLoader.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/BasePhpFormulaLoader.php new file mode 100644 index 0000000..a205a03 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/BasePhpFormulaLoader.php @@ -0,0 +1,159 @@ + + */ +abstract class BasePhpFormulaLoader implements FormulaLoaderInterface +{ + protected $factory; + protected $prototypes; + + public function __construct(AssetFactory $factory) + { + $this->factory = $factory; + $this->prototypes = array(); + + foreach ($this->registerPrototypes() as $prototype => $options) { + $this->addPrototype($prototype, $options); + } + } + + public function addPrototype($prototype, array $options = array()) + { + $tokens = token_get_all('prototypes[$prototype] = array($tokens, $options); + } + + public function load(ResourceInterface $resource) + { + if (!$nbProtos = count($this->prototypes)) { + throw new \LogicException('There are no prototypes registered.'); + } + + $buffers = array_fill(0, $nbProtos, ''); + $bufferLevels = array_fill(0, $nbProtos, 0); + $buffersInWildcard = array(); + + $tokens = token_get_all($resource->getContent()); + $calls = array(); + + while ($token = array_shift($tokens)) { + $current = self::tokenToString($token); + // loop through each prototype (by reference) + foreach (array_keys($this->prototypes) as $i) { + $prototype =& $this->prototypes[$i][0]; + $options = $this->prototypes[$i][1]; + $buffer =& $buffers[$i]; + $level =& $bufferLevels[$i]; + + if (isset($buffersInWildcard[$i])) { + switch ($current) { + case '(': ++$level; break; + case ')': --$level; break; + } + + $buffer .= $current; + + if (!$level) { + $calls[] = array($buffer.';', $options); + $buffer = ''; + unset($buffersInWildcard[$i]); + } + } elseif ($current == self::tokenToString(current($prototype))) { + $buffer .= $current; + if ('*' == self::tokenToString(next($prototype))) { + $buffersInWildcard[$i] = true; + ++$level; + } + } else { + reset($prototype); + unset($buffersInWildcard[$i]); + $buffer = ''; + } + } + } + + $formulae = array(); + foreach ($calls as $call) { + $formulae += call_user_func_array(array($this, 'processCall'), $call); + } + + return $formulae; + } + + private function processCall($call, array $protoOptions = array()) + { + $tmp = tempnam(sys_get_temp_dir(), 'assetic'); + file_put_contents($tmp, implode("\n", array( + 'registerSetupCode(), + $call, + 'echo serialize($_call);', + ))); + $args = unserialize(shell_exec('php '.escapeshellarg($tmp))); + unlink($tmp); + + $inputs = isset($args[0]) ? self::argumentToArray($args[0]) : array(); + $filters = isset($args[1]) ? self::argumentToArray($args[1]) : array(); + $options = isset($args[2]) ? $args[2] : array(); + + if (!isset($options['debug'])) { + $options['debug'] = $this->factory->isDebug(); + } + + if (!is_array($options)) { + throw new \RuntimeException('The third argument must be omitted, null or an array.'); + } + + // apply the prototype options + $options += $protoOptions; + + if (!isset($options['name'])) { + $options['name'] = $this->factory->generateAssetName($inputs, $filters, $options); + } + + return array($options['name'] => array($inputs, $filters, $options)); + } + + /** + * Returns an array of prototypical calls and options. + * + * @return array Prototypes and options + */ + abstract protected function registerPrototypes(); + + /** + * Returns setup code for the reflection scriptlet. + * + * @return string Some PHP setup code + */ + abstract protected function registerSetupCode(); + + static protected function tokenToString($token) + { + return is_array($token) ? $token[1] : $token; + } + + static protected function argumentToArray($argument) + { + return is_array($argument) ? $argument : array_filter(array_map('trim', explode(',', $argument))); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/CachedFormulaLoader.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/CachedFormulaLoader.php new file mode 100644 index 0000000..174fdd2 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/CachedFormulaLoader.php @@ -0,0 +1,68 @@ + + */ +class CachedFormulaLoader implements FormulaLoaderInterface +{ + private $loader; + private $configCache; + private $debug; + + /** + * Constructor. + * + * When the loader is in debug mode it will ensure the cached formulae + * are fresh before returning them. + * + * @param FormulaLoaderInterface $loader A formula loader + * @param ConfigCache $configCache A config cache + * @param Boolean $debug The debug mode + */ + public function __construct(FormulaLoaderInterface $loader, ConfigCache $configCache, $debug = false) + { + $this->loader = $loader; + $this->configCache = $configCache; + $this->debug = $debug; + } + + public function load(ResourceInterface $resources) + { + if (!$resources instanceof IteratorResourceInterface) { + $resources = array($resources); + } + + $formulae = array(); + + foreach ($resources as $resource) { + $id = (string) $resource; + if (!$this->configCache->has($id) || ($this->debug && !$resource->isFresh($this->configCache->getTimestamp($id)))) { + $formulae += $this->loader->load($resource); + $this->configCache->set($id, $formulae); + } else { + $formulae += $this->configCache->get($id); + } + } + + return $formulae; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FormulaLoaderInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FormulaLoaderInterface.php new file mode 100644 index 0000000..f1e877d --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FormulaLoaderInterface.php @@ -0,0 +1,34 @@ + + */ +interface FormulaLoaderInterface +{ + /** + * Loads formulae from a resource. + * + * Formulae should be loaded the same regardless of the current debug + * mode. Debug considerations should happen downstream. + * + * @param ResourceInterface $resource A resource + * + * @return array An array of formulae + */ + function load(ResourceInterface $resource); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FunctionCallsFormulaLoader.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FunctionCallsFormulaLoader.php new file mode 100644 index 0000000..9147806 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Loader/FunctionCallsFormulaLoader.php @@ -0,0 +1,53 @@ + + */ +class FunctionCallsFormulaLoader extends BasePhpFormulaLoader +{ + protected function registerPrototypes() + { + return array( + 'assetic_javascripts(*)' => array('output' => 'js/*.js'), + 'assetic_stylesheets(*)' => array('output' => 'css/*.css'), + 'assetic_image(*)' => array('output' => 'images/*'), + ); + } + + protected function registerSetupCode() + { + return <<<'EOF' +function assetic_javascripts() +{ + global $_call; + $_call = func_get_args(); +} + +function assetic_stylesheets() +{ + global $_call; + $_call = func_get_args(); +} + +function assetic_image() +{ + global $_call; + $_call = func_get_args(); +} + +EOF; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/CoalescingDirectoryResource.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/CoalescingDirectoryResource.php new file mode 100644 index 0000000..07f880e --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/CoalescingDirectoryResource.php @@ -0,0 +1,112 @@ + + */ +class CoalescingDirectoryResource implements IteratorResourceInterface +{ + private $directories; + + public function __construct($directories) + { + $this->directories = array(); + + foreach ($directories as $directory) { + $this->addDirectory($directory); + } + } + + public function addDirectory(IteratorResourceInterface $directory) + { + $this->directories[] = $directory; + } + + public function isFresh($timestamp) + { + foreach ($this->getFileResources() as $file) { + if (!$file->isFresh($timestamp)) { + return false; + } + } + + return true; + } + + public function getContent() + { + $parts = array(); + foreach ($this->getFileResources() as $file) { + $parts[] = $file->getContent(); + } + + return implode("\n", $parts); + } + + /** + * Returns a string to uniquely identify the current resource. + * + * @return string An identifying string + */ + public function __toString() + { + $parts = array(); + foreach ($this->directories as $directory) { + $parts[] = (string) $directory; + } + + return implode(',', $parts); + } + + public function getIterator() + { + return new \ArrayIterator($this->getFileResources()); + } + + /** + * Returns the relative version of a filename. + * + * @param ResourceInterface $file The file + * @param ResourceInterface $directory The directory + * + * @return string The name to compare with files from other directories + */ + protected function getRelativeName(ResourceInterface $file, ResourceInterface $directory) + { + return substr((string) $file, strlen((string) $directory)); + } + + /** + * Performs the coalesce. + * + * @return array An array of file resources + */ + private function getFileResources() + { + $paths = array(); + + foreach ($this->directories as $directory) { + foreach ($directory as $file) { + $relative = $this->getRelativeName($file, $directory); + + if (!isset($paths[$relative])) { + $paths[$relative] = $file; + } + } + } + + return array_values($paths); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/DirectoryResource.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/DirectoryResource.php new file mode 100644 index 0000000..3580d20 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/DirectoryResource.php @@ -0,0 +1,133 @@ + + */ +class DirectoryResource implements IteratorResourceInterface +{ + private $path; + private $pattern; + + /** + * Constructor. + * + * @param string $path A directory path + * @param string $pattern A filename pattern + */ + public function __construct($path, $pattern = null) + { + if (DIRECTORY_SEPARATOR != substr($path, -1)) { + $path .= DIRECTORY_SEPARATOR; + } + + $this->path = $path; + $this->pattern = $pattern; + } + + public function isFresh($timestamp) + { + if (!is_dir($this->path) || filemtime($this->path) > $timestamp) { + return false; + } + + foreach ($this as $resource) { + if (!$resource->isFresh($timestamp)) { + return false; + } + } + + return true; + } + + /** + * Returns the combined content of all inner resources. + */ + public function getContent() + { + $content = array(); + foreach ($this as $resource) { + $content[] = $resource->getContent(); + } + + return implode("\n", $content); + } + + public function __toString() + { + return $this->path; + } + + public function getIterator() + { + return is_dir($this->path) + ? new DirectoryResourceIterator($this->getInnerIterator()) + : new \EmptyIterator(); + } + + protected function getInnerIterator() + { + return new DirectoryResourceFilterIterator(new \RecursiveDirectoryIterator($this->path), $this->pattern); + } +} + +/** + * An iterator that converts file objects into file resources. + * + * @author Kris Wallsmith + * @access private + */ +class DirectoryResourceIterator extends \RecursiveIteratorIterator +{ + public function current() + { + return new FileResource(parent::current()->getPathname()); + } +} + +/** + * Filters files by a basename pattern. + * + * @author Kris Wallsmith + * @access private + */ +class DirectoryResourceFilterIterator extends \RecursiveFilterIterator +{ + protected $pattern; + + public function __construct(\RecursiveDirectoryIterator $iterator, $pattern = null) + { + parent::__construct($iterator); + + $this->pattern = $pattern; + } + + public function accept() + { + $file = $this->current(); + $name = $file->getBasename(); + + if ($file->isDir()) { + return '.' != $name[0]; + } else { + return null === $this->pattern || 0 < preg_match($this->pattern, $name); + } + } + + public function getChildren() + { + return new self(new \RecursiveDirectoryIterator($this->current()->getPathname()), $this->pattern); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/FileResource.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/FileResource.php new file mode 100644 index 0000000..1486f3e --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/FileResource.php @@ -0,0 +1,47 @@ + + */ +class FileResource implements ResourceInterface +{ + private $path; + + /** + * Constructor. + * + * @param string $path The path to a file + */ + public function __construct($path) + { + $this->path = $path; + } + + public function isFresh($timestamp) + { + return file_exists($this->path) && filemtime($this->path) <= $timestamp; + } + + public function getContent() + { + return file_exists($this->path) ? file_get_contents($this->path) : ''; + } + + public function __toString() + { + return $this->path; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/IteratorResourceInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/IteratorResourceInterface.php new file mode 100644 index 0000000..02d797f --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/IteratorResourceInterface.php @@ -0,0 +1,21 @@ + + */ +interface IteratorResourceInterface extends ResourceInterface, \IteratorAggregate +{ +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/ResourceInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/ResourceInterface.php new file mode 100644 index 0000000..d53ffcd --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Resource/ResourceInterface.php @@ -0,0 +1,43 @@ + + */ +interface ResourceInterface +{ + /** + * Checks if a timestamp represents the latest resource. + * + * @param integer $timestamp A UNIX timestamp + * + * @return Boolean True if the timestamp is up to date + */ + function isFresh($timestamp); + + /** + * Returns the content of the resource. + * + * @return string The content + */ + function getContent(); + + /** + * Returns a unique string for the current resource. + * + * @return string A unique string to identity the current resource + */ + function __toString(); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/EnsureFilterWorker.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/EnsureFilterWorker.php new file mode 100644 index 0000000..189cab8 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/EnsureFilterWorker.php @@ -0,0 +1,60 @@ + + * @todo A better asset-matcher mechanism + */ +class EnsureFilterWorker implements WorkerInterface +{ + const CHECK_SOURCE = 1; + const CHECK_TARGET = 2; + + private $pattern; + private $filter; + private $flags; + + /** + * Constructor. + * + * @param string $pattern A regex for checking the asset's target URL + * @param FilterInterface $filter A filter to apply if the regex matches + * @param integer $flags Flags for what to check + */ + public function __construct($pattern, FilterInterface $filter, $flags = null) + { + if (null === $flags) { + $flags = self::CHECK_SOURCE | self::CHECK_TARGET; + } + + $this->pattern = $pattern; + $this->filter = $filter; + $this->flags = $flags; + } + + public function process(AssetInterface $asset) + { + if ( + (self::CHECK_SOURCE === (self::CHECK_SOURCE & $this->flags) && preg_match($this->pattern, $asset->getSourcePath())) + || + (self::CHECK_TARGET === (self::CHECK_TARGET & $this->flags) && preg_match($this->pattern, $asset->getTargetPath())) + ) { + $asset->ensureFilter($this->filter); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/WorkerInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/WorkerInterface.php new file mode 100644 index 0000000..003e4e4 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Factory/Worker/WorkerInterface.php @@ -0,0 +1,31 @@ + + */ +interface WorkerInterface +{ + /** + * Processes an asset. + * + * @param AssetInterface $asset An asset + * + * @return AssetInterface|null May optionally return a replacement asset + */ + function process(AssetInterface $asset); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseCssFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseCssFilter.php new file mode 100644 index 0000000..0447fbc --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/BaseCssFilter.php @@ -0,0 +1,71 @@ + + */ +abstract class BaseCssFilter implements FilterInterface +{ + /** + * Filters all references -- url() and "@import" -- through a callable. + * + * @param string $content The CSS + * @param mixed $callback A PHP callable + * + * @return string The filtered CSS + */ + protected function filterReferences($content, $callback, $limit = -1, & $count = 0) + { + $content = $this->filterUrls($content, $callback, $limit, $count); + $content = $this->filterImports($content, $callback, $limit, $count, false); + + return $content; + } + + /** + * Filters all CSS url()'s through a callable. + * + * @param string $content The CSS + * @param mixed $callback A PHP callable + * @param integer $limit Limit the number of replacements + * @param integer $count Will be populated with the count + * + * @return string The filtered CSS + */ + protected function filterUrls($content, $callback, $limit = -1, & $count = 0) + { + return preg_replace_callback('/url\((["\']?)(?.*?)(\\1)\)/', $callback, $content, $limit, $count); + } + + /** + * Filters all CSS imports through a callable. + * + * @param string $content The CSS + * @param mixed $callback A PHP callable + * @param integer $limit Limit the number of replacements + * @param integer $count Will be populated with the count + * @param Boolean $includeUrl Whether to include url() in the pattern + * + * @return string The filtered CSS + */ + protected function filterImports($content, $callback, $limit = -1, & $count = 0, $includeUrl = true) + { + $pattern = $includeUrl + ? '/@import (?:url\()?(\'|"|)(?[^\'"\)\n\r]*)\1\)?;?/' + : '/@import (?!url\()(\'|"|)(?[^\'"\)\n\r]*)\1;?/'; + + return preg_replace_callback($pattern, $callback, $content, $limit, $count); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/CallablesFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CallablesFilter.php new file mode 100644 index 0000000..323d11c --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CallablesFilter.php @@ -0,0 +1,45 @@ + + */ +class CallablesFilter implements FilterInterface +{ + private $loader; + private $dumper; + + public function __construct($loader = null, $dumper = null) + { + $this->loader = $loader; + $this->dumper = $dumper; + } + + public function filterLoad(AssetInterface $asset) + { + if (null !== $callable = $this->loader) { + $callable($asset); + } + } + + public function filterDump(AssetInterface $asset) + { + if (null !== $callable = $this->dumper) { + $callable($asset); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/CoffeeScriptFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CoffeeScriptFilter.php new file mode 100644 index 0000000..13a54e7 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CoffeeScriptFilter.php @@ -0,0 +1,73 @@ + + */ +class CoffeeScriptFilter implements FilterInterface +{ + private $coffeePath; + private $nodePath; + + // coffee options + private $bare; + + public function __construct($coffeePath = '/usr/bin/coffee', $nodePath = '/usr/bin/node') + { + $this->coffeePath = $coffeePath; + $this->nodePath = $nodePath; + } + + public function setBare($bare) + { + $this->bare = $bare; + } + + public function filterLoad(AssetInterface $asset) + { + $input = tempnam(sys_get_temp_dir(), 'assetic_coffeescript'); + file_put_contents($input, $asset->getContent()); + + $pb = new ProcessBuilder(array( + $this->nodePath, + $this->coffeePath, + '-cp', + )); + + if ($this->bare) { + $pb->add('--bare'); + } + + $pb->add($input); + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 < $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } + + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/CompassFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CompassFilter.php new file mode 100644 index 0000000..cfd6f5c --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CompassFilter.php @@ -0,0 +1,346 @@ + + */ +class CompassFilter implements FilterInterface +{ + private $compassPath; + private $scss; + + // sass options + private $unixNewlines; + private $debugInfo; + private $cacheLocation; + private $noCache; + + // compass options + private $force; + private $style; + private $quiet; + private $boring; + private $noLineComments; + private $imagesDir; + private $javascriptsDir; + + // compass configuration file options + private $plugins = array(); + private $loadPaths = array(); + private $httpPath; + private $httpImagesPath; + private $generatedImagesPath; + private $httpJavascriptsPath; + + public function __construct($compassPath = '/usr/bin/compass') + { + $this->compassPath = $compassPath; + $this->cacheLocation = sys_get_temp_dir(); + + if ('cli' !== php_sapi_name()) { + $this->boring = true; + } + } + + public function setScss($scss) + { + $this->scss = $scss; + } + + // sass options setters + public function setUnixNewlines($unixNewlines) + { + $this->unixNewlines = $unixNewlines; + } + + public function setDebugInfo($debugInfo) + { + $this->debugInfo = $debugInfo; + } + + public function setCacheLocation($cacheLocation) + { + $this->cacheLocation = $cacheLocation; + } + + public function setNoCache($noCache) + { + $this->noCache = $noCache; + } + + // compass options setters + public function setForce($force) + { + $this->force = $force; + } + + public function setStyle($style) + { + $this->style = $style; + } + + public function setQuiet($quiet) + { + $this->quiet = $quiet; + } + + public function setBoring($boring) + { + $this->boring = $boring; + } + + public function setNoLineComments($noLineComments) + { + $this->noLineComments = $noLineComments; + } + + public function setImagesDir($imagesDir) + { + $this->imagesDir = $imagesDir; + } + + public function setJavascriptsDir($javascriptsDir) + { + $this->javascriptsDir = $javascriptsDir; + } + + // compass configuration file options setters + public function setPlugins(array $plugins) + { + $this->plugins = $plugins; + } + + public function addPlugin($plugin) + { + $this->plugins[] = $plugin; + } + + public function addLoadPath($loadPath) + { + $this->loadPaths[] = $loadPath; + } + + public function setHttpPath($httpPath) + { + $this->httpPath = $httpPath; + } + + public function setHttpImagesPath($httpImagesPath) + { + $this->httpImagesPath = $httpImagesPath; + } + + public function setGeneratedImagesPath($generatedImagesPath) + { + $this->generatedImagesPath = $generatedImagesPath; + } + + public function setHttpJavascriptsPath($httpJavascriptsPath) + { + $this->httpJavascriptsPath = $httpJavascriptsPath; + } + + public function filterLoad(AssetInterface $asset) + { + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + $loadPaths = $this->loadPaths; + if ($root && $path) { + $loadPaths[] = dirname($root.'/'.$path); + } + + // compass does not seems to handle symlink, so we use realpath() + $tempDir = realpath(sys_get_temp_dir()); + + $pb = new ProcessBuilder(array( + $this->compassPath, + 'compile', + $tempDir, + )); + $pb->inheritEnvironmentVariables(); + + if ($this->force) { + $pb->add('--force'); + } + + if ($this->style) { + $pb->add('--output-style')->add($this->style); + } + + if ($this->quiet) { + $pb->add('--quiet'); + } + + if ($this->boring) { + $pb->add('--boring'); + } + + if ($this->noLineComments) { + $pb->add('--no-line-comments'); + } + + // these two options are not passed into the config file + // because like this, compass adapts this to be xxx_dir or xxx_path + // whether it's an absolute path or not + if ($this->imagesDir) { + $pb->add('--images-dir')->add($this->imagesDir); + } + + if ($this->javascriptsDir) { + $pb->add('--javascripts-dir')->add($this->javascriptsDir); + } + + // options in config file + $optionsConfig = array(); + + if (!empty($loadPaths)) { + $optionsConfig['additional_import_paths'] = $loadPaths; + } + + if ($this->unixNewlines) { + $optionsConfig['sass_options']['unix_newlines'] = true; + } + + if ($this->debugInfo) { + $optionsConfig['sass_options']['debug_info'] = true; + } + + if ($this->cacheLocation) { + $optionsConfig['sass_options']['cache_location'] = $this->cacheLocation; + } + + if ($this->noCache) { + $optionsConfig['sass_options']['no_cache'] = true; + } + + if ($this->httpPath) { + $optionsConfig['http_path'] = $this->httpPath; + } + + if ($this->httpImagesPath) { + $optionsConfig['http_images_path'] = $this->httpImagesPath; + } + + if ($this->generatedImagesPath) { + $optionsConfig['generated_images_path'] = $this->generatedImagesPath; + } + + if ($this->httpJavascriptsPath) { + $optionsConfig['http_javascripts_path'] = $this->httpJavascriptsPath; + } + + // options in configuration file + if (count($optionsConfig)) { + $config = array(); + foreach ($this->plugins as $plugin) { + $config[] = sprintf("require '%s'", addcslashes($plugin, '\\')); + } + foreach ($optionsConfig as $name => $value) { + if (!is_array($value)) { + $config[] = sprintf('%s = "%s"', $name, addcslashes($value, '\\')); + } elseif (!empty($value)) { + $config[] = sprintf('%s = %s', $name, $this->formatArrayToRuby($value)); + } + } + + $configFile = tempnam($tempDir, 'assetic_compass'); + file_put_contents($configFile, implode("\n", $config)."\n"); + $pb->add('--config')->add($configFile); + } + + $pb->add('--sass-dir')->add('')->add('--css-dir')->add(''); + + // compass choose the type (sass or scss from the filename) + if (null !== $this->scss) { + $type = $this->scss ? 'scss' : 'sass'; + } elseif ($path) { + // FIXME: what if the extension is something else? + $type = pathinfo($path, PATHINFO_EXTENSION); + } else { + $type = 'scss'; + } + + $tempName = tempnam($tempDir, 'assetic_compass'); + unlink($tempName); // FIXME: don't use tempnam() here + + // input + $input = $tempName.'.'.$type; + + // work-around for https://github.com/chriseppstein/compass/issues/748 + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $input = str_replace('\\', '/', $input); + } + + $pb->add($input); + file_put_contents($input, $asset->getContent()); + + // output + $output = $tempName.'.css'; + + // it's not really usefull but... https://github.com/chriseppstein/compass/issues/376 + $pb->setEnv('HOME', sys_get_temp_dir()); + + $proc = $pb->getProcess(); + $code = $proc->run(); + + if (0 < $code) { + unlink($input); + if (isset($configFile)) { + unlink($configFile); + } + + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent(file_get_contents($output)); + + unlink($input); + unlink($output); + if (isset($configFile)) { + unlink($configFile); + } + } + + public function filterDump(AssetInterface $asset) + { + } + + private function formatArrayToRuby($array) + { + $output = array(); + + // does we have an associative array ? + if (count(array_filter(array_keys($array), "is_numeric")) != count($array)) { + foreach($array as $name => $value) { + $output[] = sprintf(' :%s => "%s"', $name, addcslashes($value, '\\')); + } + $output = "{\n".implode(",\n", $output)."\n}"; + } else { + foreach($array as $name => $value) { + $output[] = sprintf(' "%s"', addcslashes($value, '\\')); + } + $output = "[\n".implode(",\n", $output)."\n]"; + } + + return $output; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssEmbedFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssEmbedFilter.php new file mode 100644 index 0000000..7e8a80b --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssEmbedFilter.php @@ -0,0 +1,139 @@ + + */ +class CssEmbedFilter implements FilterInterface +{ + private $jarPath; + private $javaPath; + private $charset; + private $mhtml; // Enable MHTML mode. + private $mhtmlRoot; // Use as the MHTML root for the file. + private $root; // Prepends to all relative URLs. + private $skipMissing; // Don't throw an error for missing image files. + private $maxUriLength; // Maximum length for a data URI. Defaults to 32768. + private $maxImageSize; // Maximum image size (in bytes) to convert. + + public function __construct($jarPath, $javaPath = '/usr/bin/java') + { + $this->jarPath = $jarPath; + $this->javaPath = $javaPath; + } + + public function setCharset($charset) + { + $this->charset = $charset; + } + + public function setMhtml($mhtml) + { + $this->mhtml = $mhtml; + } + + public function setMhtmlRoot($mhtmlRoot) + { + $this->mhtmlRoot = $mhtmlRoot; + } + + public function setRoot($root) + { + $this->root = $root; + } + + public function setSkipMissing($skipMissing) + { + $this->skipMissing = $skipMissing; + } + + public function setMaxUriLength($maxUriLength) + { + $this->maxUriLength = $maxUriLength; + } + + public function setMaxImageSize($maxImageSize) + { + $this->maxImageSize = $maxImageSize; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $pb = new ProcessBuilder(array( + $this->javaPath, + '-jar', + $this->jarPath, + )); + + if (null !== $this->charset) { + $pb->add('--charset')->add($this->charset); + } + + if ($this->mhtml) { + $pb->add('--mhtml'); + } + + if (null !== $this->mhtmlRoot) { + $pb->add('--mhtmlroot')->add($this->mhtmlRoot); + } + + // automatically define root if not already defined + if (null === $this->root) { + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + if ($root && $path) { + $pb->add('--root')->add(dirname($root.'/'.$path)); + } + } else { + $pb->add('--root')->add($this->root); + } + + if ($this->skipMissing) { + $pb->add('--skip-missing'); + } + + if (null !== $this->maxUriLength) { + $pb->add('--max-uri-length')->add($this->maxUriLength); + } + + if (null !== $this->maxImageSize) { + $pb->add('--max-image-size')->add($this->maxImageSize); + } + + // input + $pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_cssembed')); + file_put_contents($input, $asset->getContent()); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 < $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssImportFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssImportFilter.php new file mode 100644 index 0000000..aa1a7d9 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssImportFilter.php @@ -0,0 +1,107 @@ + + */ +class CssImportFilter extends BaseCssFilter +{ + private $importFilter; + + /** + * Constructor. + * + * @param FilterInterface $importFilter Filter for each imported asset + */ + public function __construct(FilterInterface $importFilter = null) + { + $this->importFilter = $importFilter ?: new CssRewriteFilter(); + } + + public function filterLoad(AssetInterface $asset) + { + $importFilter = $this->importFilter; + $sourceRoot = $asset->getSourceRoot(); + $sourcePath = $asset->getSourcePath(); + + $callback = function($matches) use($importFilter, $sourceRoot, $sourcePath) + { + if (!$matches['url'] || null === $sourceRoot) { + return $matches[0]; + } + + $importRoot = $sourceRoot; + + if (false !== strpos($matches['url'], '://')) { + // absolute + list($importScheme, $tmp) = explode('://', $matches['url'], 2); + list($importHost, $importPath) = explode('/', $tmp, 2); + $importRoot = $importScheme.'://'.$importHost; + } elseif (0 === strpos($matches['url'], '//')) { + // protocol-relative + list($importHost, $importPath) = explode('/', substr($matches['url'], 2), 2); + $importHost = '//'.$importHost; + } elseif ('/' == $matches['url'][0]) { + // root-relative + $importPath = substr($matches['url'], 1); + } elseif (null !== $sourcePath) { + // document-relative + $importPath = $matches['url']; + if ('.' != $sourceDir = dirname($sourcePath)) { + $importPath = $sourceDir.'/'.$importPath; + } + } else { + return $matches[0]; + } + + // ignore other imports + if ('css' != pathinfo($importPath, PATHINFO_EXTENSION)) { + return $matches[0]; + } + + $importSource = $importRoot.'/'.$importPath; + if (false !== strpos($importSource, '://') || 0 === strpos($importSource, '//')) { + $import = new HttpAsset($importSource, array($importFilter), true); + } elseif (!file_exists($importSource)) { + // ignore not found imports + return $matches[0]; + } else { + $import = new FileAsset($importSource, array($importFilter), $importRoot, $importPath); + } + + $import->setTargetPath($sourcePath); + + return $import->dump(); + }; + + $content = $asset->getContent(); + $lastHash = md5($content); + + do { + $content = $this->filterImports($content, $callback); + $hash = md5($content); + } while ($lastHash != $hash && $lastHash = $hash); + + $asset->setContent($content); + } + + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssMinFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssMinFilter.php new file mode 100644 index 0000000..cc01672 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssMinFilter.php @@ -0,0 +1,74 @@ + + */ +class CssMinFilter implements FilterInterface +{ + private $filters; + private $plugins; + + public function __construct() + { + $this->filters = array(); + $this->plugins = array(); + } + + public function setFilters(array $filters) + { + $this->filters = $filters; + } + + public function setFilter($name, $value) + { + $this->filters[$name] = $value; + } + + public function setPlugins(array $plugins) + { + $this->plugins = $plugins; + } + + public function setPlugin($name, $value) + { + $this->plugins[$name] = $value; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $filters = $this->filters; + $plugins = $this->plugins; + + if (isset($filters['ImportImports']) && true === $filters['ImportImports']) { + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + if ($root && $path) { + $filters['ImportImports'] = array('BasePath' => dirname($root.'/'.$path)); + } else { + unset($filters['ImportImports']); + } + } + + $asset->setContent(\CssMin::minify($asset->getContent(), $filters, $plugins)); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssRewriteFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssRewriteFilter.php new file mode 100644 index 0000000..c431437 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/CssRewriteFilter.php @@ -0,0 +1,94 @@ + + */ +class CssRewriteFilter extends BaseCssFilter +{ + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $sourceBase = $asset->getSourceRoot(); + $sourcePath = $asset->getSourcePath(); + $targetPath = $asset->getTargetPath(); + + if (null === $sourcePath || null === $targetPath || $sourcePath == $targetPath) { + return; + } + + // learn how to get from the target back to the source + if (false !== strpos($sourceBase, '://')) { + list($scheme, $url) = explode('://', $sourceBase.'/'.$sourcePath, 2); + list($host, $path) = explode('/', $url, 2); + + $host = $scheme.'://'.$host.'/'; + $path = false === strpos($path, '/') ? '' : dirname($path); + $path .= '/'; + } else { + // assume source and target are on the same host + $host = ''; + + // pop entries off the target until it fits in the source + if ('.' == dirname($sourcePath)) { + $path = str_repeat('../', substr_count($targetPath, '/')); + } elseif ('.' == $targetDir = dirname($targetPath)) { + $path = dirname($sourcePath).'/'; + } else { + $path = ''; + while (0 !== strpos($sourcePath, $targetDir)) { + if (false !== $pos = strrpos($targetDir, '/')) { + $targetDir = substr($targetDir, 0, $pos); + $path .= '../'; + } else { + $targetDir = ''; + $path .= '../'; + break; + } + } + $path .= ltrim(substr(dirname($sourcePath).'/', strlen($targetDir)), '/'); + } + } + + $content = $this->filterReferences($asset->getContent(), function($matches) use($host, $path) + { + if (false !== strpos($matches['url'], '://') || 0 === strpos($matches['url'], '//') || 0 === strpos($matches['url'], 'data:')) { + // absolute or protocol-relative or data uri + return $matches[0]; + } + + if ('/' == $matches['url'][0]) { + // root relative + return str_replace($matches['url'], $host.$matches['url'], $matches[0]); + } + + // document relative + $url = $matches['url']; + while (0 === strpos($url, '../') && 2 <= substr_count($path, '/')) { + $path = substr($path, 0, strrpos(rtrim($path, '/'), '/') + 1); + $url = substr($url, 3); + } + + return str_replace($matches['url'], $host.$path.$url, $matches[0]); + }); + + $asset->setContent($content); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterCollection.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterCollection.php new file mode 100644 index 0000000..1ee743d --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterCollection.php @@ -0,0 +1,82 @@ + + */ +class FilterCollection implements FilterInterface, \IteratorAggregate, \Countable +{ + private $filters = array(); + + public function __construct($filters = array()) + { + foreach ($filters as $filter) { + $this->ensure($filter); + } + } + + /** + * Checks that the current collection contains the supplied filter. + * + * If the supplied filter is another filter collection, each of its + * filters will be checked. + */ + public function ensure(FilterInterface $filter) + { + if ($filter instanceof \Traversable) { + foreach ($filter as $f) { + $this->ensure($f); + } + } elseif (!in_array($filter, $this->filters, true)) { + $this->filters[] = $filter; + } + } + + public function all() + { + return $this->filters; + } + + public function clear() + { + $this->filters = array(); + } + + public function filterLoad(AssetInterface $asset) + { + foreach ($this->filters as $filter) { + $filter->filterLoad($asset); + } + } + + public function filterDump(AssetInterface $asset) + { + foreach ($this->filters as $filter) { + $filter->filterDump($asset); + } + } + + public function getIterator() + { + return new \ArrayIterator($this->filters); + } + + public function count() + { + return count($this->filters); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterInterface.php new file mode 100644 index 0000000..0d816c2 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/FilterInterface.php @@ -0,0 +1,36 @@ + + */ +interface FilterInterface +{ + /** + * Filters an asset after it has been loaded. + * + * @param AssetInterface $asset An asset + */ + function filterLoad(AssetInterface $asset); + + /** + * Filters an asset just before it's dumped. + * + * @param AssetInterface $asset An asset + */ + function filterDump(AssetInterface $asset); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/BaseCompilerFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/BaseCompilerFilter.php new file mode 100644 index 0000000..e571158 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/BaseCompilerFilter.php @@ -0,0 +1,84 @@ + + */ +abstract class BaseCompilerFilter implements FilterInterface +{ + // compilation levels + const COMPILE_WHITESPACE_ONLY = 'WHITESPACE_ONLY'; + const COMPILE_SIMPLE_OPTIMIZATIONS = 'SIMPLE_OPTIMIZATIONS'; + const COMPILE_ADVANCED_OPTIMIZATIONS = 'ADVANCED_OPTIMIZATIONS'; + + // formatting modes + const FORMAT_PRETTY_PRINT = 'pretty_print'; + const FORMAT_PRINT_INPUT_DELIMITER = 'print_input_delimiter'; + + // warning levels + const LEVEL_QUIET = 'QUIET'; + const LEVEL_DEFAULT = 'DEFAULT'; + const LEVEL_VERBOSE = 'VERBOSE'; + + protected $compilationLevel; + protected $jsExterns; + protected $externsUrl; + protected $excludeDefaultExterns; + protected $formatting; + protected $useClosureLibrary; + protected $warningLevel; + + public function setCompilationLevel($compilationLevel) + { + $this->compilationLevel = $compilationLevel; + } + + public function setJsExterns($jsExterns) + { + $this->jsExterns = $jsExterns; + } + + public function setExternsUrl($externsUrl) + { + $this->externsUrl = $externsUrl; + } + + public function setExcludeDefaultExterns($excludeDefaultExterns) + { + $this->excludeDefaultExterns = $excludeDefaultExterns; + } + + public function setFormatting($formatting) + { + $this->formatting = $formatting; + } + + public function setUseClosureLibrary($useClosureLibrary) + { + $this->useClosureLibrary = $useClosureLibrary; + } + + public function setWarningLevel($warningLevel) + { + $this->warningLevel = $warningLevel; + } + + public function filterLoad(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerApiFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerApiFilter.php new file mode 100644 index 0000000..4945961 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerApiFilter.php @@ -0,0 +1,100 @@ + + */ +class CompilerApiFilter extends BaseCompilerFilter +{ + public function filterDump(AssetInterface $asset) + { + $query = array( + 'js_code' => $asset->getContent(), + 'output_format' => 'json', + 'output_info' => 'compiled_code', + ); + + if (null !== $this->compilationLevel) { + $query['compilation_level'] = $this->compilationLevel; + } + + if (null !== $this->jsExterns) { + $query['js_externs'] = $this->jsExterns; + } + + if (null !== $this->externsUrl) { + $query['externs_url'] = $this->externsUrl; + } + + if (null !== $this->excludeDefaultExterns) { + $query['exclude_default_externs'] = $this->excludeDefaultExterns ? 'true' : 'false'; + } + + if (null !== $this->formatting) { + $query['formatting'] = $this->formatting; + } + + if (null !== $this->useClosureLibrary) { + $query['use_closure_library'] = $this->useClosureLibrary ? 'true' : 'false'; + } + + if (null !== $this->warningLevel) { + $query['warning_level'] = $this->warningLevel; + } + + if (preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'))) { + $context = stream_context_create(array('http' => array( + 'method' => 'POST', + 'header' => 'Content-Type: application/x-www-form-urlencoded', + 'content' => http_build_query($query), + ))); + + $response = file_get_contents('http://closure-compiler.appspot.com/compile', false, $context); + $data = json_decode($response); + + } elseif (defined('CURLOPT_POST') && !in_array('curl_init', explode(',', ini_get('disable_functions')))) { + + $ch = curl_init('http://closure-compiler.appspot.com/compile'); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded')); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $query); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); + $response = curl_exec($ch); + curl_close($ch); + + $data = json_decode($response); + } else { + throw new \RuntimeException("There is no known way to contact closure compiler available"); + } + + if (isset($data->serverErrors) && 0 < count($data->serverErrors)) { + // @codeCoverageIgnoreStart + throw new \RuntimeException(sprintf('The Google Closure Compiler API threw some server errors: '.print_r($data->serverErrors, true))); + // @codeCoverageIgnoreEnd + } + + if (isset($data->errors) && 0 < count($data->errors)) { + // @codeCoverageIgnoreStart + throw new \RuntimeException(sprintf('The Google Closure Compiler API threw some errors: '.print_r($data->errors, true))); + // @codeCoverageIgnoreEnd + } + + $asset->setContent($data->compiledCode); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerJarFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerJarFilter.php new file mode 100644 index 0000000..a86f06d --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GoogleClosure/CompilerJarFilter.php @@ -0,0 +1,90 @@ + + */ +class CompilerJarFilter extends BaseCompilerFilter +{ + private $jarPath; + private $javaPath; + + public function __construct($jarPath, $javaPath = '/usr/bin/java') + { + $this->jarPath = $jarPath; + $this->javaPath = $javaPath; + } + + public function filterDump(AssetInterface $asset) + { + $cleanup = array(); + + $pb = new ProcessBuilder(array( + $this->javaPath, + '-jar', + $this->jarPath, + )); + + if (null !== $this->compilationLevel) { + $pb->add('--compilation_level')->add($this->compilationLevel); + } + + if (null !== $this->jsExterns) { + $cleanup[] = $externs = tempnam(sys_get_temp_dir(), 'assetic_google_closure_compiler'); + file_put_contents($externs, $this->jsExterns); + $pb->add('--externs')->add($externs); + } + + if (null !== $this->externsUrl) { + $cleanup[] = $externs = tempnam(sys_get_temp_dir(), 'assetic_google_closure_compiler'); + file_put_contents($externs, file_get_contents($this->externsUrl)); + $pb->add('--externs')->add($externs); + } + + if (null !== $this->excludeDefaultExterns) { + $pb->add('--use_only_custom_externs'); + } + + if (null !== $this->formatting) { + $pb->add('--formatting')->add($this->formatting); + } + + if (null !== $this->useClosureLibrary) { + $pb->add('--manage_closure_dependencies'); + } + + if (null !== $this->warningLevel) { + $pb->add('--warning_level')->add($this->warningLevel); + } + + $pb->add('--js')->add($cleanup[] = $input = tempnam(sys_get_temp_dir(), 'assetic_google_closure_compiler')); + file_put_contents($input, $asset->getContent()); + + $proc = $pb->getProcess(); + $code = $proc->run(); + array_map('unlink', $cleanup); + + if (0 < $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/GssFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GssFilter.php new file mode 100644 index 0000000..895b3a2 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/GssFilter.php @@ -0,0 +1,142 @@ + + */ +class GssFilter implements FilterInterface +{ + private $jarPath; + private $javaPath; + private $allowUnrecognizedFunctions; + private $allowedNonStandardFunctions; + private $copyrightNotice; + private $define; + private $gssFunctionMapProvider; + private $inputOrientation; + private $outputOrientation; + private $prettyPrint; + + public function __construct($jarPath, $javaPath = '/usr/bin/java') + { + $this->jarPath = $jarPath; + $this->javaPath = $javaPath; + } + + public function setAllowUnrecognizedFunctions($allowUnrecognizedFunctions) + { + $this->allowUnrecognizedFunctions = $allowUnrecognizedFunctions; + } + + public function setAllowedNonStandardFunctions($allowNonStandardFunctions) + { + $this->allowedNonStandardFunctions = $allowNonStandardFunctions; + } + + public function setCopyrightNotice($copyrightNotice) + { + $this->copyrightNotice = $copyrightNotice; + } + + public function setDefine($define) + { + $this->define = $define; + } + + public function setGssFunctionMapProvider($gssFunctionMapProvider) + { + $this->gssFunctionMapProvider = $gssFunctionMapProvider; + } + + public function setInputOrientation($inputOrientation) + { + $this->inputOrientation = $inputOrientation; + } + + public function setOutputOrientation($outputOrientation) + { + $this->outputOrientation = $outputOrientation; + } + + public function setPrettyPrint($prettyPrint) + { + $this->prettyPrint = $prettyPrint; + } + + public function filterLoad(AssetInterface $asset) + { + $cleanup = array(); + + $pb = new ProcessBuilder(array( + $this->javaPath, + '-jar', + $this->jarPath, + )); + + if (null !== $this->allowUnrecognizedFunctions) { + $pb->add('--allow-unrecognized-functions'); + } + + if (null !== $this->allowedNonStandardFunctions) { + $pb->add('--allowed_non_standard_functions')->add($this->allowedNonStandardFunctions); + } + + if (null !== $this->copyrightNotice) { + $pb->add('--copyright-notice')->add($this->copyrightNotice); + } + + if (null !== $this->define) { + $pb->add('--define')->add($this->define); + } + + if (null !== $this->gssFunctionMapProvider) { + $pb->add('--gss-function-map-provider')->add($this->gssFunctionMapProvider); + } + + if (null !== $this->inputOrientation) { + $pb->add('--input-orientation')->add($this->inputOrientation); + } + + if (null !== $this->outputOrientation) { + $pb->add('--output-orientation')->add($this->outputOrientation); + } + + if (null !== $this->prettyPrint) { + $pb->add('--pretty-print'); + } + + $pb->add($cleanup[] = $input = tempnam(sys_get_temp_dir(), 'assetic_google_closure_stylesheets_compiler')); + file_put_contents($input, $asset->getContent()); + + $proc = $pb->getProcess(); + $code = $proc->run(); + array_map('unlink', $cleanup); + + if (0 < $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } + + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/HashableInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/HashableInterface.php new file mode 100644 index 0000000..6e9aed9 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/HashableInterface.php @@ -0,0 +1,29 @@ + + */ +interface HashableInterface +{ + /** + * Generates a hash for the object + * + * @return string Object hash + */ + function hash(); +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinFilter.php new file mode 100644 index 0000000..1a8a7a7 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinFilter.php @@ -0,0 +1,34 @@ + + */ +class JSMinFilter implements FilterInterface +{ + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $asset->setContent(\JSMin::minify($asset->getContent())); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinPlusFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinPlusFilter.php new file mode 100644 index 0000000..f61d446 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JSMinPlusFilter.php @@ -0,0 +1,34 @@ + + */ +class JSMinPlusFilter implements FilterInterface +{ + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $asset->setContent(\JSMinPlus::minify($asset->getContent())); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegoptimFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegoptimFilter.php new file mode 100644 index 0000000..6b97e6c --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegoptimFilter.php @@ -0,0 +1,81 @@ + + */ +class JpegoptimFilter implements FilterInterface +{ + private $jpegoptimBin; + private $stripAll; + private $max; + + /** + * Constructor. + * + * @param string $jpegoptimBin Path to the jpegoptim binary + */ + public function __construct($jpegoptimBin = '/usr/bin/jpegoptim') + { + $this->jpegoptimBin = $jpegoptimBin; + } + + public function setStripAll($stripAll) + { + $this->stripAll = $stripAll; + } + + public function setMax($max) + { + $this->max = $max; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $pb = new ProcessBuilder(array($this->jpegoptimBin)); + + if ($this->stripAll) { + $pb->add('--strip-all'); + } + + if ($this->max) { + $pb->add('--max='.$this->max); + } + + $pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_jpegoptim')); + file_put_contents($input, $asset->getContent()); + + $proc = $pb->getProcess(); + $proc->run(); + + if (false !== strpos($proc->getOutput(), 'ERROR')) { + unlink($input); + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent(file_get_contents($input)); + + unlink($input); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegtranFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegtranFilter.php new file mode 100644 index 0000000..95a3547 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/JpegtranFilter.php @@ -0,0 +1,103 @@ + + */ +class JpegtranFilter implements FilterInterface +{ + const COPY_NONE = 'none'; + const COPY_COMMENTS = 'comments'; + const COPY_ALL = 'all'; + + private $jpegtranBin; + private $optimize; + private $copy; + private $progressive; + private $restart; + + /** + * Constructor. + * + * @param string $jpegtranBin Path to the jpegtran binary + */ + public function __construct($jpegtranBin = '/usr/bin/jpegtran') + { + $this->jpegtranBin = $jpegtranBin; + } + + public function setOptimize($optimize) + { + $this->optimize = $optimize; + } + + public function setCopy($copy) + { + $this->copy = $copy; + } + + public function setProgressive($progressive) + { + $this->progressive = $progressive; + } + + public function setRestart($restart) + { + $this->restart = $restart; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $pb = new ProcessBuilder(array($this->jpegtranBin)); + + if ($this->optimize) { + $pb->add('-optimize'); + } + + if ($this->copy) { + $pb->add('-copy')->add($this->copy); + } + + if ($this->progressive) { + $pb->add('-progressive'); + } + + if (null !== $this->restart) { + $pb->add('-restart')->add($this->restart); + } + + $pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_jpegtran')); + file_put_contents($input, $asset->getContent()); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 < $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessFilter.php new file mode 100644 index 0000000..cd7e32c --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessFilter.php @@ -0,0 +1,114 @@ + + */ +class LessFilter implements FilterInterface +{ + private $nodeBin; + private $nodePaths; + private $compress; + + /** + * Constructor. + * + * @param string $nodeBin The path to the node binary + * @param array $nodePaths An array of node paths + */ + public function __construct($nodeBin = '/usr/bin/node', array $nodePaths = array()) + { + $this->nodeBin = $nodeBin; + $this->nodePaths = $nodePaths; + } + + public function setCompress($compress) + { + $this->compress = $compress; + } + + public function filterLoad(AssetInterface $asset) + { + static $format = <<<'EOF' +var less = require('less'); +var sys = require(process.binding('natives').util ? 'util' : 'sys'); + +new(less.Parser)(%s).parse(%s, function(e, tree) { + if (e) { + less.writeError(e); + process.exit(2); + } + + try { + sys.print(tree.toCSS(%s)); + } catch (e) { + less.writeError(e); + process.exit(3); + } +}); + +EOF; + + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + // parser options + $parserOptions = array(); + if ($root && $path) { + $parserOptions['paths'] = array(dirname($root.'/'.$path)); + $parserOptions['filename'] = basename($path); + } + + // tree options + $treeOptions = array(); + if (null !== $this->compress) { + $treeOptions['compress'] = $this->compress; + } + + $pb = new ProcessBuilder(); + $pb->inheritEnvironmentVariables(); + + // node.js configuration + if (0 < count($this->nodePaths)) { + $pb->setEnv('NODE_PATH', implode(':', $this->nodePaths)); + } + + $pb->add($this->nodeBin)->add($input = tempnam(sys_get_temp_dir(), 'assetic_less')); + file_put_contents($input, sprintf($format, + json_encode($parserOptions), + json_encode($asset->getContent()), + json_encode($treeOptions) + )); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 < $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } + + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessphpFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessphpFilter.php new file mode 100644 index 0000000..590f041 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/LessphpFilter.php @@ -0,0 +1,51 @@ + + * @author Kris Wallsmith + */ +class LessphpFilter implements FilterInterface +{ + private $presets = array(); + + public function setPresets(array $presets) + { + $this->presets = $presets; + } + + public function filterLoad(AssetInterface $asset) + { + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + $lc = new \lessc(); + if ($root && $path) { + $lc->importDir = dirname($root.'/'.$path); + } + + $asset->setContent($lc->parse($asset->getContent(), $this->presets)); + } + + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/OptiPngFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/OptiPngFilter.php new file mode 100644 index 0000000..19da807 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/OptiPngFilter.php @@ -0,0 +1,75 @@ + + */ +class OptiPngFilter implements FilterInterface +{ + private $optipngBin; + private $level; + + /** + * Constructor. + * + * @param string $optipngBin Path to the optipng binary + */ + public function __construct($optipngBin = '/usr/bin/optipng') + { + $this->optipngBin = $optipngBin; + } + + public function setLevel($level) + { + $this->level = $level; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $pb = new ProcessBuilder(array($this->optipngBin)); + + if (null !== $this->level) { + $pb->add('-o')->add($this->level); + } + + $pb->add('-out')->add($output = tempnam(sys_get_temp_dir(), 'assetic_optipng')); + unlink($output); + + $pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_optipng')); + file_put_contents($input, $asset->getContent()); + + $proc = $pb->getProcess(); + $code = $proc->run(); + + if (0 < $code) { + unlink($input); + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent(file_get_contents($output)); + + unlink($input); + unlink($output); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackagerFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackagerFilter.php new file mode 100644 index 0000000..c3a331b --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackagerFilter.php @@ -0,0 +1,64 @@ + + */ +class PackagerFilter implements FilterInterface +{ + private $packages; + + public function __construct(array $packages = array()) + { + $this->packages = $packages; + } + + public function addPackage($package) + { + $this->packages[] = $package; + } + + public function filterLoad(AssetInterface $asset) + { + static $manifest = <<getContent()); + + $packager = new \Packager(array_merge(array($package), $this->packages)); + $content = $packager->build(array(), array(), array('Application'.$hash)); + + unlink($package.'/package.yml'); + unlink($package.'/source.js'); + rmdir($package); + + $asset->setContent($content); + } + + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackerFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackerFilter.php new file mode 100644 index 0000000..5d6640b --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/PackerFilter.php @@ -0,0 +1,56 @@ + + */ +class PackerFilter implements FilterInterface +{ + protected $encoding = 'None'; + + protected $fastDecode = true; + + protected $specialChars = false; + + public function setEncoding($encoding) + { + $this->encoding = $encoding; + } + + public function setFastDecode($fastDecode) + { + $this->fastDecode = (bool) $fastDecode; + } + + public function setSpecialChars($specialChars) + { + $this->specialChars = (bool) $specialChars; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $packer = new \JavaScriptPacker($asset->getContent(), $this->encoding, $this->fastDecode, $this->specialChars); + $asset->setContent($packer->pack()); + } +} \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/PngoutFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/PngoutFilter.php new file mode 100644 index 0000000..8e5169a --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/PngoutFilter.php @@ -0,0 +1,128 @@ + + */ +class PngoutFilter implements FilterInterface +{ + // -c# + const COLOR_GREY = '0'; + const COLOR_RGB = '2'; + const COLOR_PAL = '3'; + const COLOR_GRAY_ALPHA = '4'; + const COLOR_RGB_ALPHA = '6'; + + // -f# + const FILTER_NONE = '0'; + const FILTER_X = '1'; + const FILTER_Y = '2'; + const FILTER_X_Y = '3'; + const FILTER_PAETH = '4'; + const FILTER_MIXED = '5'; + + // -s# + const STRATEGY_XTREME = '0'; + const STRATEGY_INTENSE = '1'; + const STRATEGY_LONGEST_MATCH = '2'; + const STRATEGY_HUFFMAN_ONLY = '3'; + const STRATEGY_UNCOMPRESSED = '4'; + + private $pngoutBin; + private $color; + private $filter; + private $strategy; + private $blockSplitThreshold; + + /** + * Constructor. + * + * @param string $pngoutBin Path to the pngout binary + */ + public function __construct($pngoutBin = '/usr/bin/pngout') + { + $this->pngoutBin = $pngoutBin; + } + + public function setColor($color) + { + $this->color = $color; + } + + public function setFilter($filter) + { + $this->filter = $filter; + } + + public function setStrategy($strategy) + { + $this->strategy = $strategy; + } + + public function setBlockSplitThreshold($blockSplitThreshold) + { + $this->blockSplitThreshold = $blockSplitThreshold; + } + + public function filterLoad(AssetInterface $asset) + { + } + + public function filterDump(AssetInterface $asset) + { + $pb = new ProcessBuilder(array($this->pngoutBin)); + + if (null !== $this->color) { + $pb->add('-c'.$this->color); + } + + if (null !== $this->filter) { + $pb->add('-f'.$this->filter); + } + + if (null !== $this->strategy) { + $pb->add('-s'.$this->strategy); + } + + if (null !== $this->blockSplitThreshold) { + $pb->add('-b'.$this->blockSplitThreshold); + } + + $pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_pngout')); + file_put_contents($input, $asset->getContent()); + + $output = tempnam(sys_get_temp_dir(), 'assetic_pngout'); + unlink($output); + $pb->add($output .= '.png'); + + $proc = $pb->getProcess(); + $code = $proc->run(); + + if (0 < $code) { + unlink($input); + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent(file_get_contents($output)); + + unlink($input); + unlink($output); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/SassFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/SassFilter.php new file mode 100644 index 0000000..5a73726 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/SassFilter.php @@ -0,0 +1,169 @@ + + */ +class SassFilter implements FilterInterface +{ + const STYLE_NESTED = 'nested'; + const STYLE_EXPANDED = 'expanded'; + const STYLE_COMPACT = 'compact'; + const STYLE_COMPRESSED = 'compressed'; + + private $sassPath; + private $unixNewlines; + private $scss; + private $style; + private $quiet; + private $debugInfo; + private $lineNumbers; + private $loadPaths = array(); + private $cacheLocation; + private $noCache; + private $compass; + + public function __construct($sassPath = '/usr/bin/sass') + { + $this->sassPath = $sassPath; + $this->cacheLocation = realpath(sys_get_temp_dir()); + } + + public function setUnixNewlines($unixNewlines) + { + $this->unixNewlines = $unixNewlines; + } + + public function setScss($scss) + { + $this->scss = $scss; + } + + public function setStyle($style) + { + $this->style = $style; + } + + public function setQuiet($quiet) + { + $this->quiet = $quiet; + } + + public function setDebugInfo($debugInfo) + { + $this->debugInfo = $debugInfo; + } + + public function setLineNumbers($lineNumbers) + { + $this->lineNumbers = $lineNumbers; + } + + public function addLoadPath($loadPath) + { + $this->loadPaths[] = $loadPath; + } + + public function setCacheLocation($cacheLocation) + { + $this->cacheLocation = $cacheLocation; + } + + public function setNoCache($noCache) + { + $this->noCache = $noCache; + } + + public function setCompass($compass) + { + $this->compass = $compass; + } + + public function filterLoad(AssetInterface $asset) + { + $pb = new ProcessBuilder(array($this->sassPath)); + + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + if ($root && $path) { + $pb->add('--load-path')->add(dirname($root.'/'.$path)); + } + + if ($this->unixNewlines) { + $pb->add('--unix-newlines'); + } + + if (true === $this->scss || (null === $this->scss && 'scss' == pathinfo($path, PATHINFO_EXTENSION))) { + $pb->add('--scss'); + } + + if ($this->style) { + $pb->add('--style')->add($this->style); + } + + if ($this->quiet) { + $pb->add('--quiet'); + } + + if ($this->debugInfo) { + $pb->add('--debug-info'); + } + + if ($this->lineNumbers) { + $pb->add('--line-numbers'); + } + + foreach ($this->loadPaths as $loadPath) { + $pb->add('--load-path')->add($loadPath); + } + + if ($this->cacheLocation) { + $pb->add('--cache-location')->add($this->cacheLocation); + } + + if ($this->noCache) { + $pb->add('--no-cache'); + } + + if ($this->compass) { + $pb->add('--compass'); + } + + // input + $pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_sass')); + file_put_contents($input, $asset->getContent()); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 < $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } + + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/ScssFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/ScssFilter.php new file mode 100644 index 0000000..6044b84 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Sass/ScssFilter.php @@ -0,0 +1,28 @@ + + */ +class ScssFilter extends SassFilter +{ + public function __construct($sassPath = '/usr/bin/sass') + { + parent::__construct($sassPath); + + $this->setScss(true); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/SprocketsFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/SprocketsFilter.php new file mode 100644 index 0000000..29bc71c --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/SprocketsFilter.php @@ -0,0 +1,148 @@ + + */ +class SprocketsFilter implements FilterInterface +{ + private $sprocketsLib; + private $rubyBin; + private $includeDirs; + private $assetRoot; + + /** + * Constructor. + * + * @param string $sprocketsLib Path to the Sprockets lib/ directory + * @param string $rubyBin Path to the ruby binary + */ + public function __construct($sprocketsLib = null, $rubyBin = '/usr/bin/ruby') + { + $this->sprocketsLib = $sprocketsLib; + $this->rubyBin = $rubyBin; + $this->includeDirs = array(); + } + + public function addIncludeDir($directory) + { + $this->includeDirs[] = $directory; + } + + public function setAssetRoot($assetRoot) + { + $this->assetRoot = $assetRoot; + } + + /** + * Hack around a bit, get the job done. + */ + public function filterLoad(AssetInterface $asset) + { + static $format = <<<'EOF' +#!/usr/bin/env ruby + +require %s +%s +options = { :load_path => [], + :source_files => [%s], + :expand_paths => false } + +%ssecretary = Sprockets::Secretary.new(options) +secretary.install_assets if options[:asset_root] +print secretary.concatenation + +EOF; + + $more = ''; + + foreach ($this->includeDirs as $directory) { + $more .= 'options[:load_path] << '.var_export($directory, true)."\n"; + } + + if (null !== $this->assetRoot) { + $more .= 'options[:asset_root] = '.var_export($this->assetRoot, true)."\n"; + } + + if ($more) { + $more .= "\n"; + } + + $tmpAsset = tempnam(sys_get_temp_dir(), 'assetic_sprockets'); + file_put_contents($tmpAsset, $asset->getContent()); + + $input = tempnam(sys_get_temp_dir(), 'assetic_sprockets'); + file_put_contents($input, sprintf($format, + $this->sprocketsLib + ? sprintf('File.join(%s, \'sprockets\')', var_export($this->sprocketsLib, true)) + : '\'sprockets\'', + $this->getHack($asset), + var_export($tmpAsset, true), + $more + )); + + $pb = new ProcessBuilder(array( + $this->rubyBin, + $input, + )); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($tmpAsset); + unlink($input); + + if (0 < $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } + + public function filterDump(AssetInterface $asset) + { + } + + private function getHack(AssetInterface $asset) + { + static $format = <<<'EOF' + +module Sprockets + class Preprocessor + protected + def pathname_for_relative_require_from(source_line) + Sprockets::Pathname.new(@environment, File.join(%s, location_from(source_line))) + end + end +end + +EOF; + + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + if ($root && $path) { + return sprintf($format, var_export(dirname($root.'/'.$path), true)); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/StylusFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/StylusFilter.php new file mode 100644 index 0000000..1ecc958 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/StylusFilter.php @@ -0,0 +1,117 @@ + + */ +class StylusFilter implements FilterInterface +{ + private $nodeBin; + private $nodePaths; + private $compress; + + /** + * Constructs filter. + * + * @param string $nodeBin The path to the node binary + * @param array $nodePaths An array of node paths + */ + public function __construct($nodeBin = '/usr/bin/node', array $nodePaths = array()) + { + $this->nodeBin = $nodeBin; + $this->nodePaths = $nodePaths; + } + + /** + * Enable output compression. + * + * @param boolean $compress + */ + public function setCompress($compress) + { + $this->compress = $compress; + } + + /** + * {@inheritdoc} + */ + public function filterLoad(AssetInterface $asset) + { + static $format = <<<'EOF' +var stylus = require('stylus'); +var sys = require(process.binding('natives').util ? 'util' : 'sys'); + +stylus(%s, %s).render(function(e, css){ + if (e) { + throw e; + } + + sys.print(css); + process.exit(0); +}); + +EOF; + + $root = $asset->getSourceRoot(); + $path = $asset->getSourcePath(); + + // parser options + $parserOptions = array(); + if ($root && $path) { + $parserOptions['paths'] = array(dirname($root.'/'.$path)); + $parserOptions['filename'] = basename($path); + } + + if (null !== $this->compress) { + $parserOptions['compress'] = $this->compress; + } + + $pb = new ProcessBuilder(); + $pb->inheritEnvironmentVariables(); + + // node.js configuration + if (0 < count($this->nodePaths)) { + $pb->setEnv('NODE_PATH', implode(':', $this->nodePaths)); + } + + $pb->add($this->nodeBin)->add($input = tempnam(sys_get_temp_dir(), 'assetic_stylus')); + file_put_contents($input, sprintf($format, + json_encode($asset->getContent()), + json_encode($parserOptions) + )); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 < $code) { + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } + + $asset->setContent($proc->getOutput()); + } + + /** + * {@inheritdoc} + */ + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJsFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJsFilter.php new file mode 100644 index 0000000..c84841a --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/UglifyJsFilter.php @@ -0,0 +1,136 @@ + + */ +class UglifyJsFilter implements FilterInterface +{ + private $uglifyJsPath; + private $nodeJsPath; + + private $noCopyright; + private $beautify; + private $unsafe; + + /** + * @param string $uglifyJsPath Absolute path to the uglifyjs executable + * @param string $nodeJsPath Absolute path to the folder containg node.js executable + */ + public function __construct($uglifyJsPath, $nodeJsPath = null) + { + $this->uglifyJsPath = $uglifyJsPath; + $this->nodeJsPath = $nodeJsPath; + } + + /** + * Removes the first block of comments as well + * @param bool $noCopyright True to enable + */ + public function setNoCopyright($noCopyright) + { + $this->noCopyright = $noCopyright; + } + + /** + * Output indented code + * @param bool $beautify True to enable + */ + public function setBeautify($beautify) + { + $this->beautify = $beautify; + } + + /** + * Enable additional optimizations that are known to be unsafe in some situations. + * @param bool $unsafe True to enable + */ + public function setUnsafe($unsafe) + { + $this->unsafe = $unsafe; + } + + /** + * @see Assetic\Filter\FilterInterface::filterLoad() + */ + public function filterLoad(AssetInterface $asset) + { + } + + /** + * Run the asset through UglifyJs + * + * @see Assetic\Filter\FilterInterface::filterDump() + */ + public function filterDump(AssetInterface $asset) + { + $executables = array(); + + if ($this->nodeJsPath !== null) { + $executables[] = $this->nodeJsPath; + } + + $executables[] = $this->uglifyJsPath; + + $pb = new ProcessBuilder($executables); + + if ($this->noCopyright) { + $pb->add('--no-copyright'); + } + + if ($this->beautify) { + $pb->add('--beautify'); + } + + if ($this->unsafe) { + $pb->add('--unsafe'); + } + + // input and output files + $input = tempnam(sys_get_temp_dir(), 'input'); + $output = tempnam(sys_get_temp_dir(), 'output'); + + file_put_contents($input, $asset->getContent()); + $pb->add('-o')->add($output)->add($input); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 < $code) { + if (file_exists($output)) { + unlink($output); + } + + if (127 === $code) { + throw new \RuntimeException('Path to node executable could not be resolved.'); + } + + throw FilterException::fromProcess($proc)->setInput($asset->getContent()); + } elseif (!file_exists($output)) { + throw new \RuntimeException('Error creating output file.'); + } + + $uglifiedJs = file_get_contents($output); + unlink($output); + + $asset->setContent($uglifiedJs); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/BaseCompressorFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/BaseCompressorFilter.php new file mode 100644 index 0000000..3d541d8 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/BaseCompressorFilter.php @@ -0,0 +1,108 @@ + + */ +abstract class BaseCompressorFilter implements FilterInterface +{ + private $jarPath; + private $javaPath; + private $charset; + private $lineBreak; + + public function __construct($jarPath, $javaPath = '/usr/bin/java') + { + $this->jarPath = $jarPath; + $this->javaPath = $javaPath; + } + + public function setCharset($charset) + { + $this->charset = $charset; + } + + public function setLineBreak($lineBreak) + { + $this->lineBreak = $lineBreak; + } + + public function filterLoad(AssetInterface $asset) + { + } + + /** + * Compresses a string. + * + * @param string $content The content to compress + * @param string $type The type of content, either "js" or "css" + * @param array $options An indexed array of additional options + * + * @return string The compressed content + */ + protected function compress($content, $type, $options = array()) + { + $pb = new ProcessBuilder(array( + $this->javaPath, + '-jar', + $this->jarPath, + )); + + foreach ($options as $option) { + $pb->add($option); + } + + if (null !== $this->charset) { + $pb->add('--charset')->add($this->charset); + } + + if (null !== $this->lineBreak) { + $pb->add('--line-break')->add($this->lineBreak); + } + + // input and output files + $tempDir = realpath(sys_get_temp_dir()); + $hash = substr(sha1(time().rand(11111, 99999)), 0, 7); + $input = $tempDir.DIRECTORY_SEPARATOR.$hash.'.'.$type; + $output = $tempDir.DIRECTORY_SEPARATOR.$hash.'-min.'.$type; + file_put_contents($input, $content); + $pb->add('-o')->add($output)->add($input); + + $proc = $pb->getProcess(); + $code = $proc->run(); + unlink($input); + + if (0 < $code) { + if (file_exists($output)) { + unlink($output); + } + + throw FilterException::fromProcess($proc)->setInput($content); + } elseif (!file_exists($output)) { + throw new \RuntimeException('Error creating output file.'); + } + + $retval = file_get_contents($output); + unlink($output); + + return $retval; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/CssCompressorFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/CssCompressorFilter.php new file mode 100644 index 0000000..26f7646 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/CssCompressorFilter.php @@ -0,0 +1,28 @@ + + */ +class CssCompressorFilter extends BaseCompressorFilter +{ + public function filterDump(AssetInterface $asset) + { + $asset->setContent($this->compress($asset->getContent(), 'css')); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/JsCompressorFilter.php b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/JsCompressorFilter.php new file mode 100644 index 0000000..db48cf5 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Filter/Yui/JsCompressorFilter.php @@ -0,0 +1,61 @@ + + */ +class JsCompressorFilter extends BaseCompressorFilter +{ + private $nomunge; + private $preserveSemi; + private $disableOptimizations; + + public function setNomunge($nomunge = true) + { + $this->nomunge = $nomunge; + } + + public function setPreserveSemi($preserveSemi) + { + $this->preserveSemi = $preserveSemi; + } + + public function setDisableOptimizations($disableOptimizations) + { + $this->disableOptimizations = $disableOptimizations; + } + + public function filterDump(AssetInterface $asset) + { + $options = array(); + + if ($this->nomunge) { + $options[] = '--nomunge'; + } + + if ($this->preserveSemi) { + $options[] = '--preserve-semi'; + } + + if ($this->disableOptimizations) { + $options[] = '--disable-optimizations'; + } + + $asset->setContent($this->compress($asset->getContent(), 'js', $options)); + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/FilterManager.php b/vendor/kriswallsmith/assetic/src/Assetic/FilterManager.php new file mode 100644 index 0000000..ddf6cac --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/FilterManager.php @@ -0,0 +1,64 @@ + + */ +class FilterManager +{ + private $filters = array(); + + public function set($alias, FilterInterface $filter) + { + $this->checkName($alias); + + $this->filters[$alias] = $filter; + } + + public function get($alias) + { + if (!isset($this->filters[$alias])) { + throw new \InvalidArgumentException(sprintf('There is no "%s" filter.', $alias)); + } + + return $this->filters[$alias]; + } + + public function has($alias) + { + return isset($this->filters[$alias]); + } + + public function getNames() + { + return array_keys($this->filters); + } + + /** + * Checks that a name is valid. + * + * @param string $name An asset name candidate + * + * @throws InvalidArgumentException If the asset name is invalid + */ + protected function checkName($name) + { + if (!ctype_alnum(str_replace('_', '', $name))) { + throw new \InvalidArgumentException(sprintf('The name "%s" is invalid.', $name)); + } + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Util/PathUtils.php b/vendor/kriswallsmith/assetic/src/Assetic/Util/PathUtils.php new file mode 100644 index 0000000..244d1a7 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Util/PathUtils.php @@ -0,0 +1,31 @@ + + */ +abstract class PathUtils +{ + public static function resolvePath($path, array $vars, array $values) + { + $map = array(); + foreach ($vars as $var) { + if (false === strpos($path, '{'.$var.'}')) { + continue; + } + + if (!isset($values[$var])) { + throw new \InvalidArgumentException(sprintf('The path "%s" contains the variable "%s", but was not given any value for it.', $path, $var)); + } + + $map['{'.$var.'}'] = $values[$var]; + } + + return strtr($path, $map); + } + + private final function __construct() { } +} \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/src/Assetic/Util/TraversableString.php b/vendor/kriswallsmith/assetic/src/Assetic/Util/TraversableString.php new file mode 100644 index 0000000..9f1a633 --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/Util/TraversableString.php @@ -0,0 +1,44 @@ + + */ +class TraversableString implements \IteratorAggregate, \Countable +{ + private $one; + private $many; + + public function __construct($one, array $many) + { + $this->one = $one; + $this->many = $many; + } + + public function getIterator() + { + return new \ArrayIterator($this->many); + } + + public function count() + { + return count($this->many); + } + + public function __toString() + { + return (string) $this->one; + } +} diff --git a/vendor/kriswallsmith/assetic/src/Assetic/ValueSupplierInterface.php b/vendor/kriswallsmith/assetic/src/Assetic/ValueSupplierInterface.php new file mode 100644 index 0000000..ee87b9b --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/Assetic/ValueSupplierInterface.php @@ -0,0 +1,29 @@ + + */ +interface ValueSupplierInterface +{ + /** + * Returns a map of values. + * + * @return array + */ + function getValues(); +} \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/src/functions.php b/vendor/kriswallsmith/assetic/src/functions.php new file mode 100644 index 0000000..264589e --- /dev/null +++ b/vendor/kriswallsmith/assetic/src/functions.php @@ -0,0 +1,121 @@ +factory = $factory; +} + +/** + * Returns an array of javascript URLs. + * + * @param array|string $inputs Input strings + * @param array|string $filters Filter names + * @param array $options An array of options + * + * @return array An array of javascript URLs + */ +function assetic_javascripts($inputs = array(), $filters = array(), array $options = array()) +{ + if (!isset($options['output'])) { + $options['output'] = 'js/*.js'; + } + + return _assetic_urls($inputs, $filters, $options); +} + +/** + * Returns an array of stylesheet URLs. + * + * @param array|string $inputs Input strings + * @param array|string $filters Filter names + * @param array $options An array of options + * + * @return array An array of stylesheet URLs + */ +function assetic_stylesheets($inputs = array(), $filters = array(), array $options = array()) +{ + if (!isset($options['output'])) { + $options['output'] = 'css/*.css'; + } + + return _assetic_urls($inputs, $filters, $options); +} + +/** + * Returns an image URL. + * + * @param string $input An input + * @param array|string $filters Filter names + * @param array $options An array of options + * + * @return string An image URL + */ +function assetic_image($input, $filters = array(), array $options = array()) +{ + if (!isset($options['output'])) { + $options['output'] = 'images/*'; + } + + $urls = _assetic_urls($input, $filters, $options); + + return current($urls); +} + +/** + * Returns an array of asset urls. + * + * @param array|string $inputs Input strings + * @param array|string $filters Filter names + * @param array $options An array of options + * + * @return array An array of URLs + */ +function _assetic_urls($inputs = array(), $filters = array(), array $options = array()) +{ + global $_assetic; + + if (!is_array($inputs)) { + $inputs = array_filter(array_map('trim', explode(',', $inputs))); + } + + if (!is_array($filters)) { + $filters = array_filter(array_map('trim', explode(',', $filters))); + } + + $coll = $_assetic->factory->createAsset($inputs, $filters, $options); + + $debug = isset($options['debug']) ? $options['debug'] : $_assetic->factory->isDebug(); + $combine = isset($options['combine']) ? $options['combine'] : !$debug; + + $one = $coll->getTargetPath(); + if ($combine) { + $many = array(); + foreach ($coll as $leaf) { + $many[] = $leaf->getTargetPath(); + } + } else { + $many = array($one); + } + + return new TraversableString($one, $many); +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/AssetCacheTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/AssetCacheTest.php new file mode 100644 index 0000000..9484f2a --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/AssetCacheTest.php @@ -0,0 +1,175 @@ +inner = $this->getMock('Assetic\\Asset\\AssetInterface'); + $this->cache = $this->getMock('Assetic\\Cache\\CacheInterface'); + + $this->asset = new AssetCache($this->inner, $this->cache); + } + + public function testLoadFromCache() + { + $content = 'asdf'; + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + + $this->inner->expects($this->once()) + ->method('getFilters') + ->will($this->returnValue(array($filter))); + $this->cache->expects($this->once()) + ->method('has') + ->with($this->isType('string')) + ->will($this->returnValue(true)); + $this->cache->expects($this->once()) + ->method('get') + ->with($this->isType('string')) + ->will($this->returnValue($content)); + $this->inner->expects($this->once()) + ->method('setContent') + ->with($content); + + $this->asset->load($filter); + } + + public function testLoadToCache() + { + $content = 'asdf'; + + $this->inner->expects($this->once()) + ->method('getFilters') + ->will($this->returnValue(array())); + $this->cache->expects($this->once()) + ->method('has') + ->with($this->isType('string')) + ->will($this->returnValue(false)); + $this->inner->expects($this->once())->method('load'); + $this->inner->expects($this->once()) + ->method('getContent') + ->will($this->returnValue($content)); + $this->cache->expects($this->once()) + ->method('set') + ->with($this->isType('string'), $content); + + $this->asset->load(); + } + + public function testDumpFromCache() + { + $content = 'asdf'; + + $this->inner->expects($this->once()) + ->method('getFilters') + ->will($this->returnValue(array())); + $this->cache->expects($this->once()) + ->method('has') + ->with($this->isType('string')) + ->will($this->returnValue(true)); + $this->cache->expects($this->once()) + ->method('get') + ->with($this->isType('string')) + ->will($this->returnValue($content)); + + $this->assertEquals($content, $this->asset->dump(), '->dump() returns the cached value'); + } + + public function testDumpToCache() + { + $content = 'asdf'; + + $this->inner->expects($this->once()) + ->method('getFilters') + ->will($this->returnValue(array())); + $this->cache->expects($this->once()) + ->method('has') + ->with($this->isType('string')) + ->will($this->returnValue(false)); + $this->inner->expects($this->once()) + ->method('dump') + ->will($this->returnValue($content)); + $this->cache->expects($this->once()) + ->method('set') + ->with($this->isType('string'), $content); + + $this->assertEquals($content, $this->asset->dump(), '->dump() returns the dumped value'); + } + + public function testEnsureFilter() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + $this->inner->expects($this->once())->method('ensureFilter'); + $this->asset->ensureFilter($filter); + } + + public function testGetFilters() + { + $this->inner->expects($this->once()) + ->method('getFilters') + ->will($this->returnValue(array())); + + $this->assertInternalType('array', $this->asset->getFilters(), '->getFilters() returns the inner asset filters'); + } + + public function testGetContent() + { + $this->inner->expects($this->once()) + ->method('getContent') + ->will($this->returnValue('asdf')); + + $this->assertEquals('asdf', $this->asset->getContent(), '->getContent() returns the inner asset content'); + } + + public function testSetContent() + { + $this->inner->expects($this->once()) + ->method('setContent') + ->with('asdf'); + + $this->asset->setContent('asdf'); + } + + public function testGetSourceRoot() + { + $this->inner->expects($this->once()) + ->method('getSourceRoot') + ->will($this->returnValue('asdf')); + + $this->assertEquals('asdf', $this->asset->getSourceRoot(), '->getSourceRoot() returns the inner asset source root'); + } + + public function testGetSourcePath() + { + $this->inner->expects($this->once()) + ->method('getSourcePath') + ->will($this->returnValue('asdf')); + + $this->assertEquals('asdf', $this->asset->getSourcePath(), '->getSourcePath() returns the inner asset source path'); + } + + public function testGetLastModified() + { + $this->inner->expects($this->once()) + ->method('getLastModified') + ->will($this->returnValue(123)); + + $this->assertEquals(123, $this->asset->getLastModified(), '->getLastModified() returns the inner asset last modified'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/AssetCollectionTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/AssetCollectionTest.php new file mode 100644 index 0000000..9dd095f --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/AssetCollectionTest.php @@ -0,0 +1,318 @@ +assertInstanceOf('Assetic\\Asset\\AssetInterface', $coll, 'AssetCollection implements AssetInterface'); + } + + public function testLoadFilter() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + $filter->expects($this->once())->method('filterLoad'); + + $coll = new AssetCollection(array(new StringAsset('')), array($filter)); + $coll->load(); + } + + public function testDumpFilter() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + $filter->expects($this->once())->method('filterDump'); + + $coll = new AssetCollection(array(new StringAsset('')), array($filter)); + $coll->dump(); + } + + public function testNestedCollectionLoad() + { + $content = 'foobar'; + + $count = 0; + $matches = array(); + $filter = new CallablesFilter(function($asset) use ($content, & $matches, & $count) + { + ++$count; + if ($content == $asset->getContent()) { + $matches[] = $asset; + } + }); + + $innerColl = new AssetCollection(array(new StringAsset($content))); + $outerColl = new AssetCollection(array($innerColl), array($filter)); + $outerColl->load(); + + $this->assertEquals(1, count($matches), '->load() applies filters to leaves'); + $this->assertEquals(1, $count, '->load() applies filters to leaves only'); + } + + public function testMixedIteration() + { + $asset = new StringAsset('asset'); + $nestedAsset = new StringAsset('nested'); + $innerColl = new AssetCollection(array($nestedAsset)); + + $contents = array(); + $filter = new CallablesFilter(function($asset) use(& $contents) + { + $contents[] = $asset->getContent(); + }); + + $coll = new AssetCollection(array($asset, $innerColl), array($filter)); + $coll->load(); + + $this->assertEquals(array('asset', 'nested'), $contents, '->load() iterates over multiple levels'); + } + + public function testLoadDedupBySourceUrl() + { + $asset1 = new StringAsset('asset', array(), '/some/dir', 'foo.bar'); + $asset2 = new StringAsset('asset', array(), '/some/dir', 'foo.bar'); + + $coll = new AssetCollection(array($asset1, $asset2)); + $coll->load(); + + $this->assertEquals('asset', $coll->getContent(), '->load() detects duplicate assets based on source URL'); + } + + public function testLoadDedupByStrictEquality() + { + $asset = new StringAsset('foo'); + + $coll = new AssetCollection(array($asset, $asset)); + $coll->load(); + + $this->assertEquals('foo', $coll->getContent(), '->load() detects duplicate assets based on strict equality'); + } + + public function testDumpDedupBySourceUrl() + { + $asset1 = new StringAsset('asset', array(), '/some/dir', 'foo.bar'); + $asset2 = new StringAsset('asset', array(), '/some/dir', 'foo.bar'); + + $coll = new AssetCollection(array($asset1, $asset2)); + $coll->load(); + + $this->assertEquals('asset', $coll->dump(), '->dump() detects duplicate assets based on source URL'); + } + + public function testDumpDedupByStrictEquality() + { + $asset = new StringAsset('foo'); + + $coll = new AssetCollection(array($asset, $asset)); + $coll->load(); + + $this->assertEquals('foo', $coll->dump(), '->dump() detects duplicate assets based on strict equality'); + } + + public function testIterationFilters() + { + $count = 0; + $filter = new CallablesFilter(function() use(&$count) { ++$count; }); + + $coll = new AssetCollection(); + $coll->add(new StringAsset('')); + $coll->ensureFilter($filter); + + foreach ($coll as $asset) { + $asset->dump(); + } + + $this->assertEquals(1, $count, 'collection filters are called when child assets are iterated over'); + } + + public function testSetContent() + { + $coll = new AssetCollection(); + $coll->setContent('asdf'); + + $this->assertEquals('asdf', $coll->getContent(), '->setContent() sets the content'); + } + + /** + * @dataProvider getTimestampsAndExpected + */ + public function testGetLastModified($timestamps, $expected) + { + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + + for ($i = 0; $i < count($timestamps); $i++) { + $asset->expects($this->at($i)) + ->method('getLastModified') + ->will($this->returnValue($timestamps[$i])); + } + + $coll = new AssetCollection(array_fill(0, count($timestamps), $asset)); + + $this->assertEquals($expected, $coll->getLastModified(), '->getLastModifed() returns the highest last modified'); + } + + public function getTimestampsAndExpected() + { + return array( + array(array(1, 2, 3), 3), + array(array(5, 4, 3), 5), + array(array(3, 8, 5), 8), + array(array(3, 8, null), 8), + ); + } + + public function testRecursiveIteration() + { + $asset1 = $this->getMock('Assetic\\Asset\\AssetInterface'); + $asset2 = $this->getMock('Assetic\\Asset\\AssetInterface'); + $asset3 = $this->getMock('Assetic\\Asset\\AssetInterface'); + $asset4 = $this->getMock('Assetic\\Asset\\AssetInterface'); + + $coll3 = new AssetCollection(array($asset1, $asset2)); + $coll2 = new AssetCollection(array($asset3, $coll3)); + $coll1 = new AssetCollection(array($asset4, $coll2)); + + $i = 0; + foreach ($coll1 as $a) { + $i++; + } + + $this->assertEquals(4, $i, 'iteration with a recursive iterator is recursive'); + } + + public function testRecursiveDeduplication() + { + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + + $coll3 = new AssetCollection(array($asset, $asset)); + $coll2 = new AssetCollection(array($asset, $coll3)); + $coll1 = new AssetCollection(array($asset, $coll2)); + + $i = 0; + foreach ($coll1 as $a) { + $i++; + } + + $this->assertEquals(1, $i, 'deduplication is performed recursively'); + } + + public function testIteration() + { + $asset1 = new StringAsset('asset1', array(), '/some/dir', 'foo.css'); + $asset2 = new StringAsset('asset2', array(), '/some/dir', 'foo.css'); + $asset3 = new StringAsset('asset3', array(), '/some/dir', 'bar.css'); + + $coll = new AssetCollection(array($asset1, $asset2, $asset3)); + + $count = 0; + foreach ($coll as $a) { + ++$count; + } + + $this->assertEquals(2, $count, 'iterator filters duplicates based on url'); + } + + public function testBasenameCollision() + { + $asset1 = new StringAsset('asset1', array(), '/some/dir', 'foo/foo.css'); + $asset2 = new StringAsset('asset2', array(), '/some/dir', 'bar/foo.css'); + + $coll = new AssetCollection(array($asset1, $asset2)); + + $urls = array(); + foreach ($coll as $leaf) { + $urls[] = $leaf->getTargetPath(); + } + + $this->assertEquals(2, count(array_unique($urls)), 'iterator prevents basename collisions'); + } + + public function testEmptyMtime() + { + $coll = new AssetCollection(); + $this->assertNull($coll->getLastModified(), '->getLastModified() returns null on empty collection'); + } + + public function testLeafManipulation() + { + $coll = new AssetCollection(array(new StringAsset('asdf'))); + + foreach ($coll as $leaf) { + $leaf->setTargetPath('asdf'); + } + + foreach ($coll as $leaf) { + $this->assertEquals('asdf', $leaf->getTargetPath(), 'leaf changes persist between iterations'); + } + } + + public function testRemoveLeaf() + { + $coll = new AssetCollection(array( + $leaf = new StringAsset('asdf'), + )); + + $this->assertTrue($coll->removeLeaf($leaf)); + } + + public function testRemoveRecursiveLeaf() + { + $coll = new AssetCollection(array( + new AssetCollection(array( + $leaf = new StringAsset('asdf'), + )) + )); + + $this->assertTrue($coll->removeLeaf($leaf)); + } + + public function testRemoveInvalidLeaf() + { + $this->setExpectedException('InvalidArgumentException'); + + $coll = new AssetCollection(); + $coll->removeLeaf(new StringAsset('asdf')); + } + + public function testReplaceLeaf() + { + $coll = new AssetCollection(array( + $leaf = new StringAsset('asdf'), + )); + + $this->assertTrue($coll->replaceLeaf($leaf, new StringAsset('foo'))); + } + + public function testReplaceRecursiveLeaf() + { + $coll = new AssetCollection(array( + new AssetCollection(array( + $leaf = new StringAsset('asdf'), + )), + )); + + $this->assertTrue($coll->replaceLeaf($leaf, new StringAsset('foo'))); + } + + public function testReplaceInvalidLeaf() + { + $this->setExpectedException('InvalidArgumentException'); + + $coll = new AssetCollection(); + $coll->replaceLeaf(new StringAsset('foo'), new StringAsset('bar')); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/AssetReferenceTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/AssetReferenceTest.php new file mode 100644 index 0000000..3a62b77 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/AssetReferenceTest.php @@ -0,0 +1,126 @@ +am = $this->getMock('Assetic\\AssetManager'); + $this->ref = new AssetReference($this->am, 'foo'); + } + + /** + * @dataProvider getMethodAndRetVal + */ + public function testMethods($method, $returnValue) + { + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + + $this->am->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($asset)); + $asset->expects($this->once()) + ->method($method) + ->will($this->returnValue($returnValue)); + + $this->assertEquals($returnValue, $this->ref->$method(), '->'.$method.'() returns the asset value'); + } + + public function getMethodAndRetVal() + { + return array( + array('getContent', 'asdf'), + array('getSourceRoot', 'asdf'), + array('getSourcePath', 'asdf'), + array('getTargetPath', 'asdf'), + array('getLastModified', 123), + ); + } + + public function testLazyFilters() + { + $this->am->expects($this->never())->method('get'); + $this->ref->ensureFilter($this->getMock('Assetic\\Filter\\FilterInterface')); + } + + public function testFilterFlush() + { + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + + $this->am->expects($this->exactly(2)) + ->method('get') + ->with('foo') + ->will($this->returnValue($asset)); + $asset->expects($this->once())->method('ensureFilter'); + $asset->expects($this->once()) + ->method('getFilters') + ->will($this->returnValue(array())); + + $this->ref->ensureFilter($this->getMock('Assetic\\Filter\\FilterInterface')); + + $this->assertInternalType('array', $this->ref->getFilters(), '->getFilters() flushes and returns filters'); + } + + public function testSetContent() + { + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + + $this->am->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($asset)); + $asset->expects($this->once()) + ->method('setContent') + ->with('asdf'); + + $this->ref->setContent('asdf'); + } + + public function testLoad() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + + $this->am->expects($this->exactly(2)) + ->method('get') + ->with('foo') + ->will($this->returnValue($asset)); + $asset->expects($this->once()) + ->method('load') + ->with($filter); + + $this->ref->load($filter); + } + + public function testDump() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + + $this->am->expects($this->exactly(2)) + ->method('get') + ->with('foo') + ->will($this->returnValue($asset)); + $asset->expects($this->once()) + ->method('dump') + ->with($filter); + + $this->ref->dump($filter); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/FileAssetTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/FileAssetTest.php new file mode 100644 index 0000000..c11d4c0 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/FileAssetTest.php @@ -0,0 +1,72 @@ +assertInstanceOf('Assetic\\Asset\\AssetInterface', $asset, 'Asset implements AssetInterface'); + } + + public function testLazyLoading() + { + $asset = new FileAsset(__FILE__); + $this->assertEmpty($asset->getContent(), 'The asset content is empty before load'); + + $asset->load(); + $this->assertNotEmpty($asset->getContent(), 'The asset content is not empty after load'); + } + + public function testGetLastModifiedType() + { + $asset = new FileAsset(__FILE__); + $this->assertInternalType('integer', $asset->getLastModified(), '->getLastModified() returns an integer'); + } + + public function testGetLastModifiedTypeFileNotFound() + { + $asset = new FileAsset(__DIR__ . "/foo/bar/baz.css"); + + $this->setExpectedException("RuntimeException", "The source file"); + $asset->getLastModified(); + } + + public function testGetLastModifiedValue() + { + $asset = new FileAsset(__FILE__); + $this->assertLessThan(time(), $asset->getLastModified(), '->getLastModified() returns the mtime'); + } + + public function testDefaultBaseAndPath() + { + $asset = new FileAsset(__FILE__); + $this->assertEquals(__DIR__, $asset->getSourceRoot(), '->__construct() defaults base to the asset directory'); + $this->assertEquals(basename(__FILE__), $asset->getSourcePath(), '->__construct() defaults path to the asset basename'); + } + + public function testPathGuessing() + { + $asset = new FileAsset(__FILE__, array(), __DIR__); + $this->assertEquals(basename(__FILE__), $asset->getSourcePath(), '->__construct() guesses the asset path'); + } + + public function testInvalidBase() + { + $this->setExpectedException('InvalidArgumentException'); + + $asset = new FileAsset(__FILE__, array(), __DIR__.'/foo'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/GlobAssetTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/GlobAssetTest.php new file mode 100644 index 0000000..e0d7199 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/GlobAssetTest.php @@ -0,0 +1,61 @@ +assertInstanceOf('Assetic\\Asset\\AssetInterface', $asset, 'Asset implements AssetInterface'); + } + + public function testIteration() + { + $assets = new GlobAsset(__DIR__.'/*.php'); + $this->assertGreaterThan(0, iterator_count($assets), 'GlobAsset initializes for iteration'); + } + + public function testRecursiveIteration() + { + $assets = new GlobAsset(__DIR__.'/*.php'); + $this->assertGreaterThan(0, iterator_count($assets), 'GlobAsset initializes for recursive iteration'); + } + + public function testGetLastModifiedType() + { + $assets = new GlobAsset(__DIR__.'/*.php'); + $this->assertInternalType('integer', $assets->getLastModified(), '->getLastModified() returns an integer'); + } + + public function testGetLastModifiedValue() + { + $assets = new GlobAsset(__DIR__.'/*.php'); + $this->assertLessThan(time(), $assets->getLastModified(), '->getLastModified() returns a file mtime'); + } + + public function testLoad() + { + $assets = new GlobAsset(__DIR__.'/*.php'); + $assets->load(); + + $this->assertNotEmpty($assets->getContent(), '->load() loads contents'); + } + + public function testDump() + { + $assets = new GlobAsset(__DIR__.'/*.php'); + $this->assertNotEmpty($assets->dump(), '->dump() dumps contents'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/HttpAssetTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/HttpAssetTest.php new file mode 100644 index 0000000..960492a --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/HttpAssetTest.php @@ -0,0 +1,58 @@ +markTestSkipped('The OpenSSL extension is not loaded.'); + } + + $asset = new HttpAsset(self::JQUERY); + $this->assertInternalType('integer', $asset->getLastModified(), '->getLastModified() returns an integer'); + } + + public function testProtocolRelativeUrl() + { + $asset = new HttpAsset(substr(self::JQUERY, 6)); + $asset->load(); + $this->assertNotEmpty($asset->getContent()); + } + + public function testMalformedUrl() + { + $this->setExpectedException('InvalidArgumentException'); + + new HttpAsset(__FILE__); + } + + public function testInvalidUrl() + { + $this->setExpectedException('RuntimeException'); + + $asset = new HttpAsset('http://invalid.com/foobar'); + $asset->load(); + } + + public function testSourceMetadata() + { + $asset = new HttpAsset(self::JQUERY); + $this->assertEquals('https://ajax.googleapis.com', $asset->getSourceRoot(), '->__construct() set the source root'); + $this->assertEquals('ajax/libs/jquery/1.6.1/jquery.min.js', $asset->getSourcePath(), '->__construct() set the source path'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/StringAssetTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/StringAssetTest.php new file mode 100644 index 0000000..7994a38 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Asset/StringAssetTest.php @@ -0,0 +1,79 @@ +assertInstanceOf('Assetic\\Asset\\AssetInterface', $asset, 'Asset implements AssetInterface'); + } + + public function testLoadAppliesFilters() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + $filter->expects($this->once())->method('filterLoad'); + + $asset = new StringAsset('foo', array($filter)); + $asset->load(); + } + + public function testAutomaticLoad() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + $filter->expects($this->once())->method('filterLoad'); + + $asset = new StringAsset('foo', array($filter)); + $asset->dump(); + } + + public function testGetFilters() + { + $asset = new StringAsset(''); + $this->assertInternalType('array', $asset->getFilters(), '->getFilters() returns an array'); + } + + public function testLoadAppliesAdditionalFilter() + { + $asset = new StringAsset(''); + $asset->load(); + + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + $filter->expects($this->once()) + ->method('filterLoad') + ->with($asset); + + $asset->load($filter); + } + + public function testDumpAppliesAdditionalFilter() + { + $asset = new StringAsset(''); + + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + $filter->expects($this->once()) + ->method('filterDump') + ->with($asset); + + $asset->dump($filter); + } + + public function testLastModified() + { + $asset = new StringAsset(''); + $asset->setLastModified(123); + $this->assertEquals(123, $asset->getLastModified(), '->getLastModified() return the set last modified value'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/AssetManagerTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/AssetManagerTest.php new file mode 100644 index 0000000..d459855 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/AssetManagerTest.php @@ -0,0 +1,53 @@ +am = new AssetManager(); + } + + public function testGetAsset() + { + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + $this->am->set('foo', $asset); + $this->assertSame($asset, $this->am->get('foo'), '->get() returns an asset'); + } + + public function testGetInvalidAsset() + { + $this->setExpectedException('InvalidArgumentException'); + $this->am->get('foo'); + } + + public function testHas() + { + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + $this->am->set('foo', $asset); + + $this->assertTrue($this->am->has('foo'), '->has() returns true if the asset is set'); + $this->assertFalse($this->am->has('bar'), '->has() returns false if the asset is not set'); + } + + public function testInvalidName() + { + $this->setExpectedException('InvalidArgumentException'); + + $this->am->set('@foo', $this->getMock('Assetic\\Asset\\AssetInterface')); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/AssetWriterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/AssetWriterTest.php new file mode 100644 index 0000000..ed8061e --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/AssetWriterTest.php @@ -0,0 +1,206 @@ +dir = sys_get_temp_dir().'/assetic_tests_'.rand(11111, 99999); + mkdir($this->dir); + $this->writer = new AssetWriter($this->dir, array( + 'locale' => array('en', 'de', 'fr'), + 'browser' => array('ie', 'firefox', 'other'), + 'gzip' => array('gzip', '') + )); + } + + protected function tearDown() + { + array_map('unlink', glob($this->dir.'/*')); + rmdir($this->dir); + } + + public function testWriteManagerAssets() + { + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + $am = $this->getMock('Assetic\\AssetManager'); + + $am->expects($this->once()) + ->method('getNames') + ->will($this->returnValue(array('foo'))); + $am->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($asset)); + $asset->expects($this->atLeastOnce()) + ->method('getTargetPath') + ->will($this->returnValue('target_url')); + $asset->expects($this->once()) + ->method('dump') + ->will($this->returnValue('content')); + $asset->expects($this->atLeastOnce()) + ->method('getVars') + ->will($this->returnValue(array())); + $asset->expects($this->atLeastOnce()) + ->method('getValues') + ->will($this->returnValue(array())); + + $this->writer->writeManagerAssets($am); + + $this->assertFileExists($this->dir.'/target_url'); + $this->assertEquals('content', file_get_contents($this->dir.'/target_url')); + } + + public function testWriteAssetWithVars() + { + $asset = $this->getMock('Assetic\Asset\AssetInterface'); + $asset->expects($this->atLeastOnce()) + ->method('getVars') + ->will($this->returnValue(array('locale'))); + + $self = $this; + $expectedValues = array( + array('locale' => 'en'), + array('locale' => 'de'), + array('locale' => 'fr'), + ); + $asset->expects($this->exactly(3)) + ->method('setValues') + ->will($this->returnCallback(function($values) use($self, $expectedValues) { + static $counter = 0; + $self->assertEquals($expectedValues[$counter++], $values); + })); + $asset->expects($this->exactly(3)) + ->method('getValues') + ->will($this->returnCallback(function() use($expectedValues) { + static $counter = 0; + return $expectedValues[$counter++]; + })); + + $asset->expects($this->exactly(3)) + ->method('dump') + ->will($this->onConsecutiveCalls('en', 'de', 'fr')); + + $asset->expects($this->atLeastOnce()) + ->method('getTargetPath') + ->will($this->returnValue('target.{locale}')); + + $this->writer->writeAsset($asset); + + $this->assertFileExists($this->dir.'/target.en'); + $this->assertFileExists($this->dir.'/target.de'); + $this->assertFileExists($this->dir.'/target.fr'); + $this->assertEquals('en', file_get_contents($this->dir.'/target.en')); + $this->assertEquals('de', file_get_contents($this->dir.'/target.de')); + $this->assertEquals('fr', file_get_contents($this->dir.'/target.fr')); + } + + public function testAssetWithInputVars() + { + $asset = new FileAsset(__DIR__.'/Fixture/messages.{locale}.js', + array(), null, null, array('locale')); + $asset->setTargetPath('messages.{locale}.js'); + + $this->writer->writeAsset($asset); + + $this->assertFileExists($this->dir.'/messages.en.js'); + $this->assertFileExists($this->dir.'/messages.de.js'); + $this->assertFileExists($this->dir.'/messages.fr.js'); + $this->assertEquals('var messages = {"text.greeting": "Hello %name%!"};', + file_get_contents($this->dir.'/messages.en.js')); + $this->assertEquals('var messages = {"text.greeting": "Hallo %name%!"};', + file_get_contents($this->dir.'/messages.de.js')); + $this->assertEquals('var messages = {"text.greet": "All\u00f4 %name%!"};', + file_get_contents($this->dir.'/messages.fr.js')); + } + + /** + * @dataProvider getCombinationTests + */ + public function testGetCombinations($vars, $expectedCombinations) + { + $ref = new \ReflectionMethod($this->writer, 'getCombinations'); + $ref->setAccessible(true); + + $this->assertEquals($expectedCombinations, $ref->invoke($this->writer, $vars)); + } + + public function getCombinationTests() + { + $tests = array(); + + // no variables + $tests[] = array( + array(), + array(array()) + ); + + // one variables + $tests[] = array( + array('locale'), + array( + array('locale' => 'en'), + array('locale' => 'de'), + array('locale' => 'fr'), + ) + ); + + // two variables + $tests[] = array( + array('locale', 'browser'), + array( + array('locale' => 'en', 'browser' => 'ie'), + array('locale' => 'de', 'browser' => 'ie'), + array('locale' => 'fr', 'browser' => 'ie'), + array('locale' => 'en', 'browser' => 'firefox'), + array('locale' => 'de', 'browser' => 'firefox'), + array('locale' => 'fr', 'browser' => 'firefox'), + array('locale' => 'en', 'browser' => 'other'), + array('locale' => 'de', 'browser' => 'other'), + array('locale' => 'fr', 'browser' => 'other'), + ) + ); + + // three variables + $tests[] = array( + array('locale', 'browser', 'gzip'), + array( + array('locale' => 'en', 'browser' => 'ie', 'gzip' => 'gzip'), + array('locale' => 'de', 'browser' => 'ie', 'gzip' => 'gzip'), + array('locale' => 'fr', 'browser' => 'ie', 'gzip' => 'gzip'), + array('locale' => 'en', 'browser' => 'firefox', 'gzip' => 'gzip'), + array('locale' => 'de', 'browser' => 'firefox', 'gzip' => 'gzip'), + array('locale' => 'fr', 'browser' => 'firefox', 'gzip' => 'gzip'), + array('locale' => 'en', 'browser' => 'other', 'gzip' => 'gzip'), + array('locale' => 'de', 'browser' => 'other', 'gzip' => 'gzip'), + array('locale' => 'fr', 'browser' => 'other', 'gzip' => 'gzip'), + array('locale' => 'en', 'browser' => 'ie', 'gzip' => ''), + array('locale' => 'de', 'browser' => 'ie', 'gzip' => ''), + array('locale' => 'fr', 'browser' => 'ie', 'gzip' => ''), + array('locale' => 'en', 'browser' => 'firefox', 'gzip' => ''), + array('locale' => 'de', 'browser' => 'firefox', 'gzip' => ''), + array('locale' => 'fr', 'browser' => 'firefox', 'gzip' => ''), + array('locale' => 'en', 'browser' => 'other', 'gzip' => ''), + array('locale' => 'de', 'browser' => 'other', 'gzip' => ''), + array('locale' => 'fr', 'browser' => 'other', 'gzip' => ''), + ) + ); + + return $tests; + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Cache/ApcCacheTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Cache/ApcCacheTest.php new file mode 100644 index 0000000..659c06f --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Cache/ApcCacheTest.php @@ -0,0 +1,42 @@ +markTestSkipped('APC must be installed and enabled.'); + } + } + + public function testCache() + { + $cache = new ApcCache(); + + $this->assertFalse($cache->has('foo')); + + $cache->set('foo', 'bar'); + $this->assertEquals('bar', $cache->get('foo')); + + $this->assertTrue($cache->has('foo')); + + $cache->remove('foo'); + $this->assertFalse($cache->has('foo')); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Cache/ConfigCacheTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Cache/ConfigCacheTest.php new file mode 100644 index 0000000..78ab541 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Cache/ConfigCacheTest.php @@ -0,0 +1,65 @@ +dir = sys_get_temp_dir().'/assetic/tests/config_cache'; + $this->cache = new ConfigCache($this->dir); + } + + protected function tearDown() + { + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->dir, \FilesystemIterator::SKIP_DOTS)) as $file) { + unlink($file->getPathname()); + } + } + + public function testCache() + { + $this->cache->set('foo', array(1, 2, 3)); + $this->assertEquals(array(1, 2, 3), $this->cache->get('foo'), '->get() returns the ->set() value'); + } + + public function testTimestamp() + { + $this->cache->set('bar', array(4, 5, 6)); + $this->assertInternalType('integer', $time = $this->cache->getTimestamp('bar'), '->getTimestamp() returns an integer'); + $this->assertNotEmpty($time, '->getTimestamp() returns a non-empty number'); + } + + public function testInvalidValue() + { + $this->setExpectedException('RuntimeException'); + $this->cache->get('_invalid'); + } + + public function testInvalidTimestamp() + { + $this->setExpectedException('RuntimeException'); + $this->cache->getTimestamp('_invalid'); + } + + public function testHas() + { + $this->cache->set('foo', 'bar'); + $this->assertTrue($this->cache->has('foo')); + $this->assertFalse($this->cache->has('_invalid')); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Cache/ExpiringCacheTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Cache/ExpiringCacheTest.php new file mode 100644 index 0000000..b67cab3 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Cache/ExpiringCacheTest.php @@ -0,0 +1,111 @@ +inner = $this->getMock('Assetic\\Cache\\CacheInterface'); + $this->lifetime = 3600; + $this->cache = new ExpiringCache($this->inner, $this->lifetime); + } + + public function testHasExpired() + { + $key = 'asdf'; + $expiresKey = 'asdf.expires'; + $thePast = 0; + + $this->inner->expects($this->once()) + ->method('has') + ->with($key) + ->will($this->returnValue(true)); + $this->inner->expects($this->once()) + ->method('get') + ->with($expiresKey) + ->will($this->returnValue($thePast)); + $this->inner->expects($this->at(2)) + ->method('remove') + ->with($expiresKey); + $this->inner->expects($this->at(3)) + ->method('remove') + ->with($key); + + $this->assertFalse($this->cache->has($key), '->has() returns false if an expired value exists'); + } + + public function testHasNotExpired() + { + $key = 'asdf'; + $expiresKey = 'asdf.expires'; + $theFuture = time() * 2; + + $this->inner->expects($this->once()) + ->method('has') + ->with($key) + ->will($this->returnValue(true)); + $this->inner->expects($this->once()) + ->method('get') + ->with($expiresKey) + ->will($this->returnValue($theFuture)); + + $this->assertTrue($this->cache->has($key), '->has() returns true if a value the not expired'); + } + + public function testSetLifetime() + { + $key = 'asdf'; + $expiresKey = 'asdf.expires'; + $value = 'qwerty'; + + $this->inner->expects($this->at(0)) + ->method('set') + ->with($expiresKey, $this->greaterThanOrEqual(time() + $this->lifetime)); + $this->inner->expects($this->at(1)) + ->method('set') + ->with($key, $value); + + $this->cache->set($key, $value); + } + + public function testRemove() + { + $key = 'asdf'; + $expiresKey = 'asdf.expires'; + + $this->inner->expects($this->at(0)) + ->method('remove') + ->with($expiresKey); + $this->inner->expects($this->at(1)) + ->method('remove') + ->with($key); + + $this->cache->remove($key); + } + + public function testGet() + { + $this->inner->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue('bar')); + + $this->assertEquals('bar', $this->cache->get('foo'), '->get() returns the cached value'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Cache/FilesystemCacheTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Cache/FilesystemCacheTest.php new file mode 100644 index 0000000..daf41e9 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Cache/FilesystemCacheTest.php @@ -0,0 +1,52 @@ +assertFalse($cache->has('foo')); + + $cache->set('foo', 'bar'); + $this->assertEquals('bar', $cache->get('foo')); + + $this->assertTrue($cache->has('foo')); + + $cache->remove('foo'); + $this->assertFalse($cache->has('foo')); + } + + public function testSetCreatesDir() + { + $dir = sys_get_temp_dir().'/assetic/fscachetest'; + + $tearDown = function() use($dir) + { + array_map('unlink', glob($dir.'/*')); + @rmdir($dir); + }; + + $tearDown(); + + $cache = new FilesystemCache($dir); + $cache->set('foo', 'bar'); + + $this->assertFileExists($dir.'/foo'); + + $tearDown(); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/AsseticExtensionTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/AsseticExtensionTest.php new file mode 100644 index 0000000..360e03a --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/AsseticExtensionTest.php @@ -0,0 +1,204 @@ +markTestSkipped('Twig is not installed.'); + } + + $this->am = $this->getMock('Assetic\\AssetManager'); + $this->fm = $this->getMock('Assetic\\FilterManager'); + + $this->valueSupplier = $this->getMock('Assetic\ValueSupplierInterface'); + + $this->factory = new AssetFactory(__DIR__.'/templates'); + $this->factory->setAssetManager($this->am); + $this->factory->setFilterManager($this->fm); + + $this->twig = new \Twig_Environment(); + $this->twig->setLoader(new \Twig_Loader_Filesystem(__DIR__.'/templates')); + $this->twig->addExtension(new AsseticExtension($this->factory, array(), $this->valueSupplier)); + } + + public function testReference() + { + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + $this->am->expects($this->any()) + ->method('get') + ->with('foo') + ->will($this->returnValue($asset)); + + $xml = $this->renderXml('reference.twig'); + $this->assertEquals(1, count($xml->asset)); + $this->assertStringStartsWith('css/', (string) $xml->asset['url']); + } + + public function testGlob() + { + $xml = $this->renderXml('glob.twig'); + $this->assertEquals(1, count($xml->asset)); + $this->assertStringStartsWith('css/', (string) $xml->asset['url']); + } + + public function testAbsolutePath() + { + $xml = $this->renderXml('absolute_path.twig'); + $this->assertEquals(1, count($xml->asset)); + $this->assertStringStartsWith('css/', (string) $xml->asset['url']); + } + + public function testFilters() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + + $this->fm->expects($this->at(0)) + ->method('get') + ->with('foo') + ->will($this->returnValue($filter)); + $this->fm->expects($this->at(1)) + ->method('get') + ->with('bar') + ->will($this->returnValue($filter)); + + $xml = $this->renderXml('filters.twig'); + $this->assertEquals(1, count($xml->asset)); + $this->assertStringStartsWith('css/', (string) $xml->asset['url']); + } + + public function testOptionalFilter() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + + $this->fm->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($filter)); + + $xml = $this->renderXml('optional_filter.twig'); + $this->assertEquals(1, count($xml->asset)); + $this->assertStringStartsWith('css/', (string) $xml->asset['url']); + } + + public function testOutputPattern() + { + $xml = $this->renderXml('output_pattern.twig'); + $this->assertEquals(1, count($xml->asset)); + $this->assertStringStartsWith('css/packed/', (string) $xml->asset['url']); + $this->assertStringEndsWith('.css', (string) $xml->asset['url']); + } + + public function testOutput() + { + $xml = $this->renderXml('output_url.twig'); + $this->assertEquals(1, count($xml->asset)); + $this->assertEquals('explicit_url.css', (string) $xml->asset['url']); + } + + public function testMixture() + { + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + $this->am->expects($this->any()) + ->method('get') + ->with('foo') + ->will($this->returnValue($asset)); + + $xml = $this->renderXml('mixture.twig'); + $this->assertEquals(1, count($xml->asset)); + $this->assertEquals('packed/mixture', (string) $xml->asset['url']); + } + + public function testDebug() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + + $this->fm->expects($this->once()) + ->method('get') + ->with('bar') + ->will($this->returnValue($filter)); + + $xml = $this->renderXml('debug.twig'); + $this->assertEquals(2, count($xml->asset)); + $this->assertStringStartsWith('css/packed_', (string) $xml->asset[0]['url']); + $this->assertStringEndsWith('.css', (string) $xml->asset[0]['url']); + } + + public function testCombine() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + + $this->fm->expects($this->once()) + ->method('get') + ->with('bar') + ->will($this->returnValue($filter)); + + $xml = $this->renderXml('combine.twig'); + $this->assertEquals(1, count($xml->asset)); + $this->assertEquals('css/packed.css', (string) $xml->asset[0]['url']); + } + + public function testImage() + { + $xml = $this->renderXml('image.twig'); + $this->assertEquals(1, count($xml->image)); + $this->assertStringEndsWith('.png', (string) $xml->image[0]['url']); + } + + public function testFilterFunction() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + + $this->fm->expects($this->once()) + ->method('get') + ->with('some_filter') + ->will($this->returnValue($filter)); + + $this->twig->addExtension(new AsseticExtension($this->factory, array( + 'some_func' => array( + 'filter' => 'some_filter', + 'options' => array('output' => 'css/*.css'), + ), + ))); + + $xml = $this->renderXml('function.twig'); + $this->assertEquals(1, count($xml->asset)); + $this->assertStringEndsWith('.css', (string) $xml->asset[0]['url']); + } + + public function testVariables() + { + $this->valueSupplier->expects($this->once()) + ->method('getValues') + ->will($this->returnValue(array('foo' => 'a', 'bar' => 'b'))); + + $xml = $this->renderXml('variables.twig'); + $this->assertEquals(2, $xml->url->count()); + $this->assertEquals("js/7d0828c_foo_1.a.b.js", (string) $xml->url[0]); + $this->assertEquals("js/7d0828c_variable_input.a_2.a.b.js", (string) $xml->url[1]); + } + + private function renderXml($name, $context = array()) + { + return new \SimpleXMLElement($this->twig->loadTemplate($name)->render($context)); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/TwigFormulaLoaderTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/TwigFormulaLoaderTest.php new file mode 100644 index 0000000..ba0f99c --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/TwigFormulaLoaderTest.php @@ -0,0 +1,97 @@ +markTestSkipped('Twig is not installed.'); + } + + $this->am = $this->getMock('Assetic\\AssetManager'); + $this->fm = $this->getMock('Assetic\\FilterManager'); + + $factory = new AssetFactory(__DIR__.'/templates'); + $factory->setAssetManager($this->am); + $factory->setFilterManager($this->fm); + + $twig = new \Twig_Environment(); + $twig->addExtension(new AsseticExtension($factory, array( + 'some_func' => array( + 'filter' => 'some_filter', + 'options' => array('output' => 'css/*.css'), + ), + ))); + + $this->loader = new TwigFormulaLoader($twig); + } + + public function testMixture() + { + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + + $expected = array( + 'mixture' => array( + array('foo', 'foo/*', '@foo'), + array(), + array( + 'output' => 'packed/mixture', + 'name' => 'mixture', + 'debug' => false, + 'combine' => null, + 'vars' => array(), + ), + ), + ); + + $resource = $this->getMock('Assetic\\Factory\\Resource\\ResourceInterface'); + $resource->expects($this->once()) + ->method('getContent') + ->will($this->returnValue(file_get_contents(__DIR__.'/templates/mixture.twig'))); + $this->am->expects($this->any()) + ->method('get') + ->with('foo') + ->will($this->returnValue($asset)); + + $formulae = $this->loader->load($resource); + $this->assertEquals($expected, $formulae); + } + + public function testFunction() + { + $expected = array( + 'my_asset' => array( + array('path/to/asset'), + array('some_filter'), + array('output' => 'css/*.css', 'name' => 'my_asset'), + ), + ); + + $resource = $this->getMock('Assetic\\Factory\\Resource\\ResourceInterface'); + $resource->expects($this->once()) + ->method('getContent') + ->will($this->returnValue(file_get_contents(__DIR__.'/templates/function.twig'))); + + $formulae = $this->loader->load($resource); + $this->assertEquals($expected, $formulae); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/TwigResourceTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/TwigResourceTest.php new file mode 100644 index 0000000..771da9f --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/TwigResourceTest.php @@ -0,0 +1,48 @@ +markTestSkipped('Twig is not installed.'); + } + } + + public function testInvalidTemplateNameGetContent() + { + $loader = $this->getMock('Twig_LoaderInterface'); + $loader->expects($this->once()) + ->method('getSource') + ->with('asdf') + ->will($this->throwException(new \Twig_Error_Loader(''))); + + $resource = new TwigResource($loader, 'asdf'); + $this->assertEquals('', $resource->getContent()); + } + + public function testInvalidTemplateNameIsFresh() + { + $loader = $this->getMock('Twig_LoaderInterface'); + $loader->expects($this->once()) + ->method('isFresh') + ->with('asdf', 1234) + ->will($this->throwException(new \Twig_Error_Loader(''))); + + $resource = new TwigResource($loader, 'asdf'); + $this->assertFalse($resource->isFresh(1234)); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/absolute_path.twig b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/absolute_path.twig new file mode 100644 index 0000000..05dc382 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/absolute_path.twig @@ -0,0 +1,3 @@ + +{% stylesheets '/path/to/something.css' as='foo' %}{% endstylesheets %} + diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/combine.twig b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/combine.twig new file mode 100644 index 0000000..e1ab5f9 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/combine.twig @@ -0,0 +1,3 @@ + +{% stylesheets 'foo.css' 'bar.css' filter='?foo,bar' output='css/packed.css' debug=true combine=true %}{% endstylesheets %} + diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/debug.twig b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/debug.twig new file mode 100644 index 0000000..550292e --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/debug.twig @@ -0,0 +1,3 @@ + +{% stylesheets 'foo.css' 'bar.css' filter='?foo,bar' output='css/packed.css' debug=true %}{% endstylesheets %} + diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/filters.twig b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/filters.twig new file mode 100644 index 0000000..d211384 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/filters.twig @@ -0,0 +1,3 @@ + +{% stylesheets 'foo' filter='foo, bar' %}{% endstylesheets %} + diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/function.twig b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/function.twig new file mode 100644 index 0000000..0284197 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/function.twig @@ -0,0 +1,3 @@ + + + diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/glob.twig b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/glob.twig new file mode 100644 index 0000000..4624933 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/glob.twig @@ -0,0 +1,3 @@ + +{% stylesheets 'css/src/*' %}{% endstylesheets %} + diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/image.twig b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/image.twig new file mode 100644 index 0000000..902ecf0 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/image.twig @@ -0,0 +1,3 @@ + +{% image 'images/foo.png' %}{% endimage %} + diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/mixture.twig b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/mixture.twig new file mode 100644 index 0000000..482e6ec --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/mixture.twig @@ -0,0 +1,3 @@ + +{% stylesheets 'foo' 'foo/*' '@foo' output='packed/*' name='mixture' %}{% endstylesheets %} + diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/optional_filter.twig b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/optional_filter.twig new file mode 100644 index 0000000..4a4dbb6 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/optional_filter.twig @@ -0,0 +1,3 @@ + +{% stylesheets 'foo' filter='?foo' %}{% endstylesheets %} + diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/output_pattern.twig b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/output_pattern.twig new file mode 100644 index 0000000..cf85897 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/output_pattern.twig @@ -0,0 +1,3 @@ + +{% stylesheets 'foo' output='css/packed/*.css' %}{% endstylesheets %} + diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/output_url.twig b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/output_url.twig new file mode 100644 index 0000000..51a2a2d --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/output_url.twig @@ -0,0 +1,3 @@ + +{% stylesheets 'foo' output='explicit_url.css' %}{% endstylesheets %} + diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/reference.twig b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/reference.twig new file mode 100644 index 0000000..371c4b7 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/reference.twig @@ -0,0 +1,3 @@ + +{% stylesheets '@foo' %}{% endstylesheets %} + diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/variables.twig b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/variables.twig new file mode 100644 index 0000000..111cff4 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Extension/Twig/templates/variables.twig @@ -0,0 +1,5 @@ + + {% javascripts "foo.js" "variable_input.{foo}.js" vars=["foo", "bar"] debug=true %} + {{ asset_url }} + {% endjavascripts %} + \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/AssetFactoryTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/AssetFactoryTest.php new file mode 100644 index 0000000..7bfa8bc --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/AssetFactoryTest.php @@ -0,0 +1,203 @@ +am = $this->getMock('Assetic\\AssetManager'); + $this->fm = $this->getMock('Assetic\\FilterManager'); + + $this->factory = new AssetFactory(__DIR__); + $this->factory->setAssetManager($this->am); + $this->factory->setFilterManager($this->fm); + } + + public function testNoAssetManagerReference() + { + $this->setExpectedException('LogicException', 'There is no asset manager.'); + + $factory = new AssetFactory('.'); + $factory->createAsset(array('@foo')); + } + + public function testNoAssetManagerNotReference() + { + $factory = new AssetFactory('.'); + $this->assertInstanceOf('Assetic\\Asset\\AssetInterface', $factory->createAsset(array('foo'))); + } + + public function testNoFilterManager() + { + $this->setExpectedException('LogicException', 'There is no filter manager.'); + + $factory = new AssetFactory('.'); + $factory->createAsset(array('foo'), array('foo')); + } + + public function testCreateAssetReference() + { + $referenced = $this->getMock('Assetic\\Asset\\AssetInterface'); + + $this->am->expects($this->any()) + ->method('get') + ->with('jquery') + ->will($this->returnValue($referenced)); + + $assets = $this->factory->createAsset(array('@jquery')); + $arr = iterator_to_array($assets); + $this->assertInstanceOf('Assetic\\Asset\\AssetReference', $arr[0], '->createAsset() creates a reference'); + } + + /** + * @dataProvider getHttpUrls + */ + public function testCreateHttpAsset($sourceUrl) + { + $assets = $this->factory->createAsset(array($sourceUrl)); + $arr = iterator_to_array($assets); + $this->assertInstanceOf('Assetic\\Asset\\HttpAsset', $arr[0], '->createAsset() creates an HTTP asset'); + } + + public function getHttpUrls() + { + return array( + array('http://example.com/foo.css'), + array('https://example.com/foo.css'), + array('//example.com/foo.css'), + ); + } + + public function testCreateFileAsset() + { + $assets = $this->factory->createAsset(array(basename(__FILE__))); + $arr = iterator_to_array($assets); + $this->assertInstanceOf('Assetic\\Asset\\FileAsset', $arr[0], '->createAsset() creates a file asset'); + } + + public function testCreateGlobAsset() + { + $assets = $this->factory->createAsset(array('*')); + $arr = iterator_to_array($assets); + $this->assertInstanceOf('Assetic\\Asset\\FileAsset', $arr[0], '->createAsset() uses a glob to create a file assets'); + } + + public function testCreateAssetCollection() + { + $asset = $this->factory->createAsset(array('*', basename(__FILE__))); + $this->assertInstanceOf('Assetic\\Asset\\AssetCollection', $asset, '->createAsset() creates an asset collection'); + } + + public function testFilter() + { + $this->fm->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($this->getMock('Assetic\\Filter\\FilterInterface'))); + + $asset = $this->factory->createAsset(array(), array('foo')); + $this->assertEquals(1, count($asset->getFilters()), '->createAsset() adds filters'); + } + + public function testInvalidFilter() + { + $this->setExpectedException('InvalidArgumentException'); + + $this->fm->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->throwException(new \InvalidArgumentException())); + + $asset = $this->factory->createAsset(array(), array('foo')); + } + + public function testOptionalInvalidFilter() + { + $this->factory->setDebug(true); + + $asset = $this->factory->createAsset(array(), array('?foo')); + + $this->assertEquals(0, count($asset->getFilters()), '->createAsset() does not add an optional invalid filter'); + } + + public function testIncludingOptionalFilter() + { + $this->fm->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($this->getMock('Assetic\\Filter\\FilterInterface'))); + + $this->factory->createAsset(array('foo.css'), array('?foo')); + } + + public function testWorkers() + { + $worker = $this->getMock('Assetic\\Factory\\Worker\\WorkerInterface'); + + // called once on the collection and once on each leaf + $worker->expects($this->exactly(3)) + ->method('process') + ->with($this->isInstanceOf('Assetic\\Asset\\AssetInterface')); + + $this->factory->addWorker($worker); + $this->factory->createAsset(array('foo.js', 'bar.js')); + } + + public function testWorkerReturn() + { + $worker = $this->getMock('Assetic\\Factory\\Worker\\WorkerInterface'); + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + + $worker->expects($this->at(2)) + ->method('process') + ->with($this->isInstanceOf('Assetic\\Asset\\AssetCollectionInterface')) + ->will($this->returnValue($asset)); + + $this->factory->addWorker($worker); + $coll = $this->factory->createAsset(array('foo.js', 'bar.js')); + + $this->assertEquals(1, count(iterator_to_array($coll))); + } + + public function testNestedFormula() + { + $this->fm->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($this->getMock('Assetic\\Filter\\FilterInterface'))); + + $inputs = array( + 'css/main.css', + array( + // nested formula + array('css/more.sass'), + array('foo'), + ), + ); + + $asset = $this->factory->createAsset($inputs, array(), array('output' => 'css/*.css')); + + $i = 0; + foreach ($asset as $leaf) { + $i++; + } + + $this->assertEquals(2, $i); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/LazyAssetManagerTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/LazyAssetManagerTest.php new file mode 100644 index 0000000..46cdf94 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/LazyAssetManagerTest.php @@ -0,0 +1,96 @@ +factory = $this->getMockBuilder('Assetic\\Factory\\AssetFactory') + ->disableOriginalConstructor() + ->getMock(); + + $this->am = new LazyAssetManager($this->factory); + } + + public function testGetFromLoader() + { + $resource = $this->getMock('Assetic\\Factory\\Resource\\ResourceInterface'); + $loader = $this->getMock('Assetic\\Factory\\Loader\\FormulaLoaderInterface'); + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + + $formula = array( + array('js/core.js', 'js/more.js'), + array('?yui_js'), + array('output' => 'js/all.js') + ); + + $loader->expects($this->once()) + ->method('load') + ->with($resource) + ->will($this->returnValue(array('foo' => $formula))); + $this->factory->expects($this->once()) + ->method('createAsset') + ->with($formula[0], $formula[1], $formula[2] + array('name' => 'foo')) + ->will($this->returnValue($asset)); + + $this->am->setLoader('foo', $loader); + $this->am->addResource($resource, 'foo'); + + $this->assertSame($asset, $this->am->get('foo'), '->get() returns an asset from the loader'); + + // test the "once" expectations + $this->am->get('foo'); + } + + public function testGetResources() + { + $resources = array( + $this->getMock('Assetic\\Factory\\Resource\\ResourceInterface'), + $this->getMock('Assetic\\Factory\\Resource\\ResourceInterface'), + ); + + $this->am->addResource($resources[0], 'foo'); + $this->am->addResource($resources[1], 'bar'); + + $ret = $this->am->getResources(); + + foreach ($resources as $resource) { + $this->assertTrue(in_array($resource, $ret, true)); + } + } + + public function testGetResourcesEmpty() + { + $this->am->getResources(); + } + + public function testSetFormula() + { + $this->am->setFormula('foo', array()); + $this->am->load(); + $this->assertTrue($this->am->hasFormula('foo'), '->load() does not remove manually added formulae'); + } + + public function testIsDebug() + { + $this->factory->expects($this->once()) + ->method('isDebug') + ->will($this->returnValue(false)); + + $this->assertSame(false, $this->am->isDebug(), '->isDebug() proxies the factory'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Loader/CachedFormulaLoaderTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Loader/CachedFormulaLoaderTest.php new file mode 100644 index 0000000..5253003 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Loader/CachedFormulaLoaderTest.php @@ -0,0 +1,138 @@ +loader = $this->getMock('Assetic\\Factory\\Loader\\FormulaLoaderInterface'); + $this->configCache = $this->getMockBuilder('Assetic\\Cache\\ConfigCache') + ->disableOriginalConstructor() + ->getMock(); + $this->resource = $this->getMock('Assetic\\Factory\\Resource\\ResourceInterface'); + } + + public function testNotDebug() + { + $expected = array( + 'foo' => array(array(), array(), array()), + 'bar' => array(array(), array(), array()), + ); + + $this->configCache->expects($this->once()) + ->method('has') + ->with($this->isType('string')) + ->will($this->returnValue(false)); + $this->loader->expects($this->once()) + ->method('load') + ->with($this->resource) + ->will($this->returnValue($expected)); + $this->configCache->expects($this->once()) + ->method('set') + ->with($this->isType('string'), $expected); + + $loader = new CachedFormulaLoader($this->loader, $this->configCache); + $this->assertEquals($expected, $loader->load($this->resource), '->load() returns formulae'); + } + + public function testNotDebugCached() + { + $expected = array( + 'foo' => array(array(), array(), array()), + 'bar' => array(array(), array(), array()), + ); + + $this->configCache->expects($this->once()) + ->method('has') + ->with($this->isType('string')) + ->will($this->returnValue(true)); + $this->resource->expects($this->never()) + ->method('isFresh'); + $this->configCache->expects($this->once()) + ->method('get') + ->with($this->isType('string')) + ->will($this->returnValue($expected)); + + $loader = new CachedFormulaLoader($this->loader, $this->configCache); + $this->assertEquals($expected, $loader->load($this->resource), '->load() returns formulae'); + } + + public function testDebugCached() + { + $timestamp = 123; + $expected = array( + 'foo' => array(array(), array(), array()), + 'bar' => array(array(), array(), array()), + ); + + $this->configCache->expects($this->once()) + ->method('has') + ->with($this->isType('string')) + ->will($this->returnValue(true)); + $this->configCache->expects($this->once()) + ->method('getTimestamp') + ->with($this->isType('string')) + ->will($this->returnValue($timestamp)); + $this->resource->expects($this->once()) + ->method('isFresh') + ->with($timestamp) + ->will($this->returnValue(true)); + $this->loader->expects($this->never()) + ->method('load'); + $this->configCache->expects($this->once()) + ->method('get') + ->with($this->isType('string')) + ->will($this->returnValue($expected)); + + $loader = new CachedFormulaLoader($this->loader, $this->configCache, true); + $this->assertEquals($expected, $loader->load($this->resource), '->load() returns formulae'); + } + + public function testDebugCachedStale() + { + $timestamp = 123; + $expected = array( + 'foo' => array(array(), array(), array()), + 'bar' => array(array(), array(), array()), + ); + + $this->configCache->expects($this->once()) + ->method('has') + ->with($this->isType('string')) + ->will($this->returnValue(true)); + $this->configCache->expects($this->once()) + ->method('getTimestamp') + ->with($this->isType('string')) + ->will($this->returnValue($timestamp)); + $this->resource->expects($this->once()) + ->method('isFresh') + ->with($timestamp) + ->will($this->returnValue(false)); + $this->loader->expects($this->once()) + ->method('load') + ->with($this->resource) + ->will($this->returnValue($expected)); + $this->configCache->expects($this->once()) + ->method('set') + ->with($this->isType('string'), $expected); + + $loader = new CachedFormulaLoader($this->loader, $this->configCache, true); + $this->assertEquals($expected, $loader->load($this->resource), '->load() returns formulae'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Loader/FunctionCallsFormulaLoaderTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Loader/FunctionCallsFormulaLoaderTest.php new file mode 100644 index 0000000..6b631bf --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Loader/FunctionCallsFormulaLoaderTest.php @@ -0,0 +1,69 @@ +getMock('Assetic\\Factory\\Resource\\ResourceInterface'); + $factory = $this->getMockBuilder('Assetic\\Factory\\AssetFactory') + ->disableOriginalConstructor() + ->getMock(); + + $resource->expects($this->once()) + ->method('getContent') + ->will($this->returnValue('')); + $factory->expects($this->once()) + ->method('generateAssetName') + ->will($this->returnValue($name)); + + $loader = new FunctionCallsFormulaLoader($factory); + $formulae = $loader->load($resource); + + $this->assertEquals($expected, $formulae); + } + + public function getJavascriptInputs() + { + return array( + array('assetic_javascripts', '"js/core.js"', 'asdf', array('asdf' => array(array('js/core.js'), array(), array('debug' => false, 'output' => 'js/*.js', 'name' => 'asdf', )))), + array('assetic_javascripts', "'js/core.js'", 'asdf', array('asdf' => array(array('js/core.js'), array(), array('debug' => false, 'output' => 'js/*.js', 'name' => 'asdf', )))), + array('assetic_javascripts', "array('js/core.js')", 'asdf', array('asdf' => array(array('js/core.js'), array(), array('debug' => false, 'output' => 'js/*.js', 'name' => 'asdf', )))), + array('assetic_javascripts', 'array("js/core.js")', 'asdf', array('asdf' => array(array('js/core.js'), array(), array('debug' => false, 'output' => 'js/*.js', 'name' => 'asdf', )))), + array('assetic_image', '"images/logo.gif"', 'asdf', array('asdf' => array(array('images/logo.gif'), array(), array('debug' => false, 'output' => 'images/*', 'name' => 'asdf')))), + ); + } + + public function testComplexFormula() + { + $factory = new AssetFactory(__DIR__.'/templates', true); + $loader = new FunctionCallsFormulaLoader($factory); + $resource = new FileResource(__DIR__.'/templates/debug.php'); + $formulae = $loader->load($resource); + + $this->assertEquals(array( + 'test123' => array( + array('foo.css', 'bar.css'), + array('?foo', 'bar'), + array('name' => 'test123', 'output' => 'css/packed.css', 'debug' => true), + ), + ), $formulae); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Loader/templates/debug.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Loader/templates/debug.php new file mode 100644 index 0000000..750c25d --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Loader/templates/debug.php @@ -0,0 +1,8 @@ + + 'test123', 'output' => 'css/packed.css', 'debug' => true)) as $url): ?> + + + diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/CoalescingDirectoryResourceTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/CoalescingDirectoryResourceTest.php new file mode 100644 index 0000000..32e8887 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/CoalescingDirectoryResourceTest.php @@ -0,0 +1,42 @@ +assertEquals(array( + realpath(__DIR__.'/Fixtures/dir1/file1.txt'), + realpath(__DIR__.'/Fixtures/dir1/file2.txt'), + realpath(__DIR__.'/Fixtures/dir2/file3.txt'), + ), $paths, 'files from multiple directories are merged'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/DirectoryResourceTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/DirectoryResourceTest.php new file mode 100644 index 0000000..bbe5cff --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/DirectoryResourceTest.php @@ -0,0 +1,108 @@ +assertTrue($resource->isFresh(time() + 5)); + $this->assertFalse($resource->isFresh(0)); + } + + /** + * @dataProvider getPatterns + */ + public function testGetContent($pattern) + { + $resource = new DirectoryResource(__DIR__, $pattern); + $content = $resource->getContent(); + + $this->assertInternalType('string', $content); + } + + public function getPatterns() + { + return array( + array(null), + array('/\.php$/'), + array('/\.foo$/'), + ); + } + + /** + * @dataProvider getPatternsAndEmpty + */ + public function testIteration($pattern, $empty) + { + $resource = new DirectoryResource(__DIR__, $pattern); + + $count = 0; + foreach ($resource as $r) { + ++$count; + $this->assertInstanceOf('Assetic\\Factory\\Resource\\ResourceInterface', $r); + } + + if ($empty) { + $this->assertEmpty($count); + } else { + $this->assertNotEmpty($count); + } + } + + public function getPatternsAndEmpty() + { + return array( + array(null, false), + array('/\.php$/', false), + array('/\.foo$/', true), + ); + } + + public function testRecursiveIteration() + { + $resource = new DirectoryResource(realpath(__DIR__.'/..'), '/^'.preg_quote(basename(__FILE__)).'$/'); + + $count = 0; + foreach ($resource as $r) { + ++$count; + } + + $this->assertEquals(1, $count); + } + + /** + * @dataProvider getPaths + */ + public function testTrailingSlash($path) + { + $resource = new DirectoryResource($path); + $this->assertStringEndsWith(DIRECTORY_SEPARATOR, (string) $resource, 'path ends with a slash'); + } + + public function getPaths() + { + return array( + array(__DIR__), + array(__DIR__.DIRECTORY_SEPARATOR), + ); + } + + public function testInvalidDirectory() + { + $resource = new DirectoryResource(__DIR__.'foo'); + $this->assertEquals(0, iterator_count($resource), 'works for non-existent directory'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/FileResourceTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/FileResourceTest.php new file mode 100644 index 0000000..4864c80 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/FileResourceTest.php @@ -0,0 +1,42 @@ +assertTrue($resource->isFresh(time() + 5)); + $this->assertFalse($resource->isFresh(0)); + } + + public function testGetContent() + { + $resource = new FileResource(__FILE__); + $this->assertEquals(file_get_contents(__FILE__), $resource->getContent()); + } + + public function testIsFreshOnInvalidPath() + { + $resource = new FileResource(__FILE__.'foo'); + $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the file does not exist'); + } + + public function testGetContentOnInvalidPath() + { + $resource = new FileResource(__FILE__.'foo'); + $this->assertSame('', $resource->getContent(), '->getContent() returns an empty string when path is invalid'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/Fixtures/dir1/file1.txt b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/Fixtures/dir1/file1.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/Fixtures/dir1/file2.txt b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/Fixtures/dir1/file2.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/Fixtures/dir2/file1.txt b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/Fixtures/dir2/file1.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/Fixtures/dir2/file3.txt b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Resource/Fixtures/dir2/file3.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Worker/EnsureFilterWorkerTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Worker/EnsureFilterWorkerTest.php new file mode 100644 index 0000000..bdcffe5 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Factory/Worker/EnsureFilterWorkerTest.php @@ -0,0 +1,47 @@ +getMock('Assetic\\Filter\\FilterInterface'); + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + + $asset->expects($this->once()) + ->method('getTargetPath') + ->will($this->returnValue('css/main.css')); + $asset->expects($this->once()) + ->method('ensureFilter') + ->with($filter); + + $worker = new EnsureFilterWorker('/\.css$/', $filter); + $worker->process($asset); + } + + public function testNonMatch() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + + $asset->expects($this->once()) + ->method('getTargetPath') + ->will($this->returnValue('js/all.js')); + $asset->expects($this->never())->method('ensureFilter'); + + $worker = new EnsureFilterWorker('/\.css$/', $filter); + $worker->process($asset); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/BaseImageFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/BaseImageFilterTest.php new file mode 100644 index 0000000..eb26c19 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/BaseImageFilterTest.php @@ -0,0 +1,24 @@ +file($data) : $finfo->buffer($data); + + self::assertEquals($expected, $actual, $message); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CallablesFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CallablesFilterTest.php new file mode 100644 index 0000000..252f2d1 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CallablesFilterTest.php @@ -0,0 +1,39 @@ +assertInstanceOf('Assetic\\Filter\\FilterInterface', $filter, 'CallablesFilter implements FilterInterface'); + } + + public function testLoader() + { + $nb = 0; + $filter = new CallablesFilter(function($asset) use(&$nb) { $nb++; }); + $filter->filterLoad($this->getMock('Assetic\\Asset\\AssetInterface')); + $this->assertEquals(1, $nb, '->filterLoad() calls the loader callable'); + } + + public function testDumper() + { + $nb = 0; + $filter = new CallablesFilter(null, function($asset) use(&$nb) { $nb++; }); + $filter->filterDump($this->getMock('Assetic\\Asset\\AssetInterface')); + $this->assertEquals(1, $nb, '->filterDump() calls the loader callable'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CoffeeScriptFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CoffeeScriptFilterTest.php new file mode 100644 index 0000000..5c46cef --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CoffeeScriptFilterTest.php @@ -0,0 +1,73 @@ +markTestSkipped('There is no COFFEE_BIN or NODE_BIN environment variable.'); + } + + $this->filter = new CoffeeScriptFilter($_SERVER['COFFEE_BIN'], $_SERVER['NODE_BIN']); + } + + public function testFilterLoad() + { + $expected = << x * x'); + $asset->load(); + + $this->filter->filterLoad($asset); + + $this->assertEquals($expected, $asset->getContent()); + } + + public function testBare() + { + $expected = << x * x'); + $asset->load(); + + $this->filter->setBare(true); + $this->filter->filterLoad($asset); + + $this->assertEquals($expected, $asset->getContent()); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CompassFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CompassFilterTest.php new file mode 100644 index 0000000..4231e8a --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CompassFilterTest.php @@ -0,0 +1,66 @@ + + * @group integration + */ +class CompassFilterTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!isset($_SERVER['COMPASS_BIN'])) { + $this->markTestSkipped('There is no COMPASS_BIN environment variable.'); + } + } + + public function testFilterLoadWithScss() + { + $asset = new FileAsset(__DIR__.'/fixtures/compass/stylesheet.scss'); + $asset->load(); + + $filter = new CompassFilter($_SERVER['COMPASS_BIN']); + $filter->filterLoad($asset); + + $this->assertContains('.test-class', $asset->getContent()); + $this->assertContains('font-size: 2em;', $asset->getContent()); + } + + public function testFilterLoadWithSass() + { + $asset = new FileAsset(__DIR__.'/fixtures/compass/stylesheet.sass'); + $asset->load(); + + $filter = new CompassFilter($_SERVER['COMPASS_BIN']); + $filter->filterLoad($asset); + + $this->assertContains('.test-class', $asset->getContent()); + $this->assertContains('font-size: 2em;', $asset->getContent()); + } + + public function testCompassMixin() + { + $asset = new FileAsset(__DIR__.'/fixtures/compass/compass.sass'); + $asset->load(); + + $filter = new CompassFilter($_SERVER['COMPASS_BIN']); + $filter->filterLoad($asset); + + $this->assertContains('text-decoration', $asset->getContent()); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CssEmbedFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CssEmbedFilterTest.php new file mode 100644 index 0000000..579abf2 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CssEmbedFilterTest.php @@ -0,0 +1,54 @@ +markTestSkipped('There is no CSSEMBED_JAR environment variable.'); + } + } + + public function testCssEmbedDataUri() + { + $data = base64_encode(file_get_contents(__DIR__.'/fixtures/home.png')); + + $asset = new FileAsset(__DIR__ . '/fixtures/cssembed/test.css'); + $asset->load(); + + $filter = new CssEmbedFilter($_SERVER['CSSEMBED_JAR']); + $filter->filterDump($asset); + + $this->assertContains('url(data:image/png;base64,'.$data, $asset->getContent()); + } + + public function testCssEmbedMhtml() + { + $asset = new FileAsset(__DIR__ . '/fixtures/cssembed/test.css'); + $asset->load(); + + $filter = new CssEmbedFilter($_SERVER['CSSEMBED_JAR']); + $filter->setMhtml(true); + $filter->setMhtmlRoot('/test'); + $filter->filterDump($asset); + + $this->assertContains('url(mhtml:/test/!', $asset->getContent()); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CssImportFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CssImportFilterTest.php new file mode 100644 index 0000000..ecde8e1 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CssImportFilterTest.php @@ -0,0 +1,67 @@ +setTargetPath('foo/bar.css'); + $asset->ensureFilter($filter1); + $asset->ensureFilter($filter2); + + $expected = <<assertEquals($expected, $asset->dump(), '->filterLoad() inlines CSS imports'); + } + + /** + * The order of these two filters is only interchangeable because one acts on + * load and the other on dump. We need a more scalable solution. + */ + public function getFilters() + { + return array( + array(new CssImportFilter(), new CssRewriteFilter()), + array(new CssRewriteFilter(), new CssImportFilter()), + ); + } + + public function testNonCssImport() + { + $asset = new FileAsset(__DIR__.'/fixtures/cssimport/noncssimport.css', array(), __DIR__.'/fixtures/cssimport', 'noncssimport.css'); + $asset->load(); + + $filter = new CssImportFilter(); + $filter->filterLoad($asset); + + $this->assertEquals(file_get_contents(__DIR__.'/fixtures/cssimport/noncssimport.css'), $asset->getContent(), '->filterLoad() skips non css'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CssMinFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CssMinFilterTest.php new file mode 100644 index 0000000..abba852 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CssMinFilterTest.php @@ -0,0 +1,40 @@ +markTestSkipped('CssMin is not installed.'); + } + } + + public function testRelativeSourceUrlImportImports() + { + $asset = new FileAsset(__DIR__.'/fixtures/cssmin/main.css'); + $asset->load(); + + $filter = new CssMinFilter(__DIR__.'/fixtures/cssmin'); + $filter->setFilter('ImportImports', true); + $filter->filterDump($asset); + + $this->assertEquals('body{color:white}body{background:black}', $asset->getContent()); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CssRewriteFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CssRewriteFilterTest.php new file mode 100644 index 0000000..4c497bf --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/CssRewriteFilterTest.php @@ -0,0 +1,124 @@ +setTargetPath($targetPath); + $asset->load(); + + $filter = new CssRewriteFilter(); + $filter->filterLoad($asset); + $filter->filterDump($asset); + + $this->assertEquals(sprintf($format, $expectedUrl), $asset->getContent(), '->filterDump() rewrites relative urls'); + } + + public function provideUrls() + { + return array( + // url variants + array('body { background: url(%s); }', 'css/body.css', 'css/build/main.css', '../images/bg.gif', '../../images/bg.gif'), + array('body { background: url("%s"); }', 'css/body.css', 'css/build/main.css', '../images/bg.gif', '../../images/bg.gif'), + array('body { background: url(\'%s\'); }', 'css/body.css', 'css/build/main.css', '../images/bg.gif', '../../images/bg.gif'), + + //url with data: + array('body { background: url(\'%s\'); }', 'css/body.css', 'css/build/main.css', 'data:image/png;base64,abcdef=', 'data:image/png;base64,abcdef='), + array('body { background: url(\'%s\'); }', 'css/body.css', 'css/build/main.css', '../images/bg-data:.gif', '../../images/bg-data:.gif'), + + // @import variants + array('@import "%s";', 'css/imports.css', 'css/build/main.css', 'import.css', '../import.css'), + array('@import url(%s);', 'css/imports.css', 'css/build/main.css', 'import.css', '../import.css'), + array('@import url("%s");', 'css/imports.css', 'css/build/main.css', 'import.css', '../import.css'), + array('@import url(\'%s\');', 'css/imports.css', 'css/build/main.css', 'import.css', '../import.css'), + + // path diffs + array('body { background: url(%s); }', 'css/body/bg.css', 'css/build/main.css', '../../images/bg.gif', '../../images/bg.gif'), + array('body { background: url(%s); }', 'css/body.css', 'main.css', '../images/bg.gif', 'css/../images/bg.gif'), // fixme + array('body { background: url(%s); }', 'body.css', 'css/main.css', 'images/bg.gif', '../images/bg.gif'), + array('body { background: url(%s); }', 'source/css/body.css', 'output/build/main.css', '../images/bg.gif', '../../source/images/bg.gif'), + array('body { background: url(%s); }', 'css/body.css', 'css/build/main.css', '//example.com/images/bg.gif', '//example.com/images/bg.gif'), + + // url diffs + array('body { background: url(%s); }', 'css/body.css', 'css/build/main.css', 'http://foo.com/bar.gif', 'http://foo.com/bar.gif'), + array('body { background: url(%s); }', 'css/body.css', 'css/build/main.css', '/images/foo.gif', '/images/foo.gif'), + array('body { background: url(%s); }', 'css/body.css', 'css/build/main.css', 'http://foo.com/images/foo.gif', 'http://foo.com/images/foo.gif'), + ); + } + + /** + * @dataProvider provideMultipleUrls + */ + public function testMultipleUrls($format, $sourcePath, $targetPath, $inputUrl1, $inputUrl2, $expectedUrl1, $expectedUrl2) + { + $asset = new StringAsset(sprintf($format, $inputUrl1, $inputUrl2), array(), null, $sourcePath); + $asset->setTargetPath($targetPath); + $asset->load(); + + $filter = new CssRewriteFilter(); + $filter->filterLoad($asset); + $filter->filterDump($asset); + + $this->assertEquals(sprintf($format, $expectedUrl1, $expectedUrl2), $asset->getContent(), '->filterDump() rewrites relative urls'); + } + + public function provideMultipleUrls() + { + return array( + // multiple url + array('body { background: url(%s); background: url(%s); }', 'css/body.css', 'css/build/main.css', '../images/bg.gif', '../images/bg2.gif', '../../images/bg.gif', '../../images/bg2.gif'), + array("body { background: url(%s);\nbackground: url(%s); }", 'css/body.css', 'css/build/main.css', '../images/bg.gif', '../images/bg2.gif', '../../images/bg.gif', '../../images/bg2.gif'), + + // multiple import + array('@import "%s"; @import "%s";', 'css/imports.css', 'css/build/main.css', 'import.css', 'import2.css', '../import.css', '../import2.css'), + array("@import \"%s\";\n@import \"%s\";", 'css/imports.css', 'css/build/main.css', 'import.css', 'import2.css', '../import.css', '../import2.css'), + + // mixed urls and imports + array('@import "%s"; body { background: url(%s); }', 'css/body.css', 'css/build/main.css', 'import.css', '../images/bg2.gif', '../import.css', '../../images/bg2.gif'), + array("@import \"%s\";\nbody { background: url(%s); }", 'css/body.css', 'css/build/main.css', 'import.css', '../images/bg2.gif', '../import.css', '../../images/bg2.gif'), + ); + } + + public function testNoTargetPath() + { + $content = 'body { background: url(foo.gif); }'; + + $asset = new StringAsset($content); + $asset->load(); + + $filter = new CssRewriteFilter(); + $filter->filterDump($asset); + + $this->assertEquals($content, $asset->getContent(), '->filterDump() urls are not changed without urls'); + } + + public function testExternalSource() + { + $asset = new StringAsset('body { background: url(../images/bg.gif); }', array(), 'http://www.example.com', 'css/main.css'); + $asset->setTargetPath('css/packed/main.css'); + $asset->load(); + + $filter = new CssRewriteFilter(); + $filter->filterDump($asset); + + $this->assertContains('http://www.example.com/css/../images/bg.gif', $asset->getContent(), '->filterDump() rewrites references in external stylesheets'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/FilterCollectionTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/FilterCollectionTest.php new file mode 100644 index 0000000..277c773 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/FilterCollectionTest.php @@ -0,0 +1,59 @@ +assertInstanceOf('Assetic\\Filter\\FilterInterface', $filter, 'FilterCollection implements FilterInterface'); + } + + public function testEnsure() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + $asset = $this->getMock('Assetic\\Asset\\AssetInterface'); + + $filter->expects($this->once())->method('filterLoad'); + + $coll = new FilterCollection(); + $coll->ensure($filter); + $coll->ensure($filter); + $coll->filterLoad($asset); + } + + public function testAll() + { + $filter = new FilterCollection(array( + $this->getMock('Assetic\\Filter\\FilterInterface'), + $this->getMock('Assetic\\Filter\\FilterInterface'), + )); + + $this->assertInternalType('array', $filter->all(), '->all() returns an array'); + } + + public function testEmptyAll() + { + $filter = new FilterCollection(); + $this->assertInternalType('array', $filter->all(), '->all() returns an array'); + } + + public function testCountable() + { + $filters = new FilterCollection(array($this->getMock('Assetic\\Filter\\FilterInterface'))); + + $this->assertEquals(1, count($filters), 'Countable returns the count'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/GoogleClosure/CompilerApiFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/GoogleClosure/CompilerApiFilterTest.php new file mode 100644 index 0000000..a8458d0 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/GoogleClosure/CompilerApiFilterTest.php @@ -0,0 +1,59 @@ +load(); + + $filter = new CompilerApiFilter(); + $filter->setCompilationLevel(CompilerApiFilter::COMPILE_SIMPLE_OPTIMIZATIONS); + $filter->setJsExterns(''); + $filter->setExternsUrl(''); + $filter->setExcludeDefaultExterns(true); + $filter->setFormatting(CompilerApiFilter::FORMAT_PRETTY_PRINT); + $filter->setUseClosureLibrary(false); + $filter->setWarningLevel(CompilerApiFilter::LEVEL_VERBOSE); + + $filter->filterLoad($asset); + $filter->filterDump($asset); + + $this->assertEquals($expected, $asset->getContent()); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/GoogleClosure/CompilerJarFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/GoogleClosure/CompilerJarFilterTest.php new file mode 100644 index 0000000..2edb9c6 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/GoogleClosure/CompilerJarFilterTest.php @@ -0,0 +1,53 @@ +markTestSkipped('There is no CLOSURE_JAR environment variable.'); + } + + $input = <<load(); + + $filter = new CompilerJarFilter($_SERVER['CLOSURE_JAR']); + $filter->filterLoad($asset); + $filter->filterDump($asset); + + $this->assertEquals($expected, $asset->getContent()); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/GssFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/GssFilterTest.php new file mode 100644 index 0000000..2b4c7bc --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/GssFilterTest.php @@ -0,0 +1,45 @@ +markTestSkipped('There is no GSS_JAR environment variable.'); + } + + $input = <<load(); + + $filter = new GssFilter($_SERVER['GSS_JAR']); + $filter->filterLoad($asset); + + $this->assertEquals($expected, $asset->getContent()); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/JSMinFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/JSMinFilterTest.php new file mode 100644 index 0000000..b0d6c21 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/JSMinFilterTest.php @@ -0,0 +1,39 @@ +markTestSkipped('JSMin is not installed.'); + } + } + + public function testRelativeSourceUrlImportImports() + { + $asset = new FileAsset(__DIR__.'/fixtures/jsmin/js.js'); + $asset->load(); + + $filter = new JSMinFilter(); + $filter->filterDump($asset); + + $this->assertEquals('var a="abc";;;var bbb="u";', $asset->getContent()); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/JSMinPlusFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/JSMinPlusFilterTest.php new file mode 100644 index 0000000..72e769a --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/JSMinPlusFilterTest.php @@ -0,0 +1,39 @@ +markTestSkipped('JSMinPlus is not installed.'); + } + } + + public function testRelativeSourceUrlImportImports() + { + $asset = new FileAsset(__DIR__.'/fixtures/jsmin/js.js'); + $asset->load(); + + $filter = new JSMinPlusFilter(); + $filter->filterDump($asset); + + $this->assertEquals('var a="abc",bbb="u"', $asset->getContent()); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/JpegoptimFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/JpegoptimFilterTest.php new file mode 100644 index 0000000..d0a6761 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/JpegoptimFilterTest.php @@ -0,0 +1,45 @@ +markTestSkipped('No jpegoptim configuration.'); + } + + $this->filter = new JpegoptimFilter($_SERVER['JPEGOPTIM_BIN']); + } + + public function testFilter() + { + $asset = new FileAsset(__DIR__.'/fixtures/home.jpg'); + $asset->load(); + + $before = $asset->getContent(); + $this->filter->filterDump($asset); + + $this->assertNotEmpty($asset->getContent(), '->filterLoad() sets content'); + $this->assertNotEquals($before, $asset->getContent(), '->filterDump() changes the content'); + $this->assertMimeType('image/jpeg', $asset->getContent(), '->filterDump() creates JPEG data'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/JpegtranFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/JpegtranFilterTest.php new file mode 100644 index 0000000..10245f6 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/JpegtranFilterTest.php @@ -0,0 +1,45 @@ +markTestSkipped('No jpegtran configuration.'); + } + + $this->filter = new JpegtranFilter($_SERVER['JPEGTRAN_BIN']); + } + + public function testFilter() + { + $asset = new FileAsset(__DIR__.'/fixtures/home.jpg'); + $asset->load(); + + $before = $asset->getContent(); + $this->filter->filterDump($asset); + + $this->assertNotEmpty($asset->getContent(), '->filterLoad() sets content'); + $this->assertNotEquals($before, $asset->getContent(), '->filterDump() changes the content'); + $this->assertMimeType('image/jpeg', $asset->getContent(), '->filterDump() creates JPEG data'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/LessFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/LessFilterTest.php new file mode 100644 index 0000000..ce97a86 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/LessFilterTest.php @@ -0,0 +1,63 @@ +markTestSkipped('No node.js configuration.'); + } + + $this->filter = new LessFilter($_SERVER['NODE_BIN'], array($_SERVER['NODE_PATH'])); + } + + public function testFilterLoad() + { + $asset = new StringAsset('.foo{.bar{width:1+1;}}'); + $asset->load(); + + $this->filter->filterLoad($asset); + + $this->assertEquals(".foo .bar {\n width: 2;\n}\n", $asset->getContent(), '->filterLoad() parses the content'); + } + + public function testImport() + { + $expected = <<load(); + + $this->filter->filterLoad($asset); + + $this->assertEquals($expected, $asset->getContent(), '->filterLoad() sets an include path based on source url'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/LessphpFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/LessphpFilterTest.php new file mode 100644 index 0000000..08a8031 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/LessphpFilterTest.php @@ -0,0 +1,67 @@ +filter = new LessphpFilter(); + } + + public function testFilterLoad() + { + $asset = new StringAsset('.foo{.bar{width:1+ 1;}}'); + $asset->load(); + + $this->filter->filterLoad($asset); + + $this->assertEquals(".foo .bar { width:2; }\n", $asset->getContent(), '->filterLoad() parses the content'); + } + + public function testImport() + { + $expected = <<load(); + + $this->filter->filterLoad($asset); + + $this->assertEquals($expected, $asset->getContent(), '->filterLoad() sets an include path based on source url'); + } + + public function testPresets() + { + $asset = new StringAsset('.foo { color: @bar }'); + $asset->load(); + + $this->filter->setPresets(array( + 'bar' => 'green' + )); + + $this->filter->filterLoad($asset); + + $this->assertEquals(".foo { color:green; }\n", $asset->getContent(), '->setPresets() to pass variables into lessphp filter'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/OptiPngFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/OptiPngFilterTest.php new file mode 100644 index 0000000..c2f72c7 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/OptiPngFilterTest.php @@ -0,0 +1,56 @@ +markTestSkipped('No OptiPNG configuration.'); + } + + $this->filter = new OptiPngFilter($_SERVER['OPTIPNG_BIN']); + } + + /** + * @dataProvider getImages + */ + public function testFilter($image) + { + $asset = new FileAsset($image); + $asset->load(); + + $before = $asset->getContent(); + $this->filter->filterDump($asset); + + $this->assertNotEmpty($asset->getContent(), '->filterDump() sets content'); + $this->assertNotEquals($before, $asset->getContent(), '->filterDump() changes the content'); + $this->assertMimeType('image/png', $asset->getContent(), '->filterDump() creates PNG data'); + } + + public function getImages() + { + return array( + array(__DIR__.'/fixtures/home.gif'), + array(__DIR__.'/fixtures/home.png'), + ); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/PackagerFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/PackagerFilterTest.php new file mode 100644 index 0000000..7dcdaa0 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/PackagerFilterTest.php @@ -0,0 +1,69 @@ +markTestSkipped('Packager is not available.'); + } + } + + public function testPackager() + { + $expected = <<load(); + + $filter = new PackagerFilter(); + $filter->addPackage(__DIR__.'/fixtures/packager/lib'); + $filter->filterLoad($asset); + + $this->assertEquals($expected, $asset->getContent(), '->filterLoad() runs packager'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/PackerFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/PackerFilterTest.php new file mode 100644 index 0000000..8bad914 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/PackerFilterTest.php @@ -0,0 +1,36 @@ +markTestSkipped('JavaScriptPacker is not installed.'); + } + } + + public function testPacker() + { + $asset = new FileAsset(__DIR__.'/fixtures/packer/example.js'); + $asset->load(); + + $filter = new PackerFilter(); + $filter->filterDump($asset); + + $this->assertEquals("var exampleFunction=function(arg1,arg2){alert('exampleFunction called!')}", $asset->getContent()); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/PngoutFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/PngoutFilterTest.php new file mode 100644 index 0000000..23c0712 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/PngoutFilterTest.php @@ -0,0 +1,57 @@ +markTestSkipped('No pngout configuration.'); + } + + $this->filter = new PngoutFilter($_SERVER['PNGOUT_BIN']); + } + + /** + * @dataProvider getImages + */ + public function testFilter($image) + { + $asset = new FileAsset($image); + $asset->load(); + + $before = $asset->getContent(); + $this->filter->filterDump($asset); + + $this->assertNotEmpty($asset->getContent(), '->filterLoad() sets content'); + $this->assertNotEquals($before, $asset->getContent(), '->filterLoad() changes the content'); + $this->assertMimeType('image/png', $asset->getContent(), '->filterLoad() creates PNG data'); + } + + public function getImages() + { + return array( + array(__DIR__.'/fixtures/home.gif'), + array(__DIR__.'/fixtures/home.jpg'), + array(__DIR__.'/fixtures/home.png'), + ); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Sass/SassFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Sass/SassFilterTest.php new file mode 100644 index 0000000..874e591 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Sass/SassFilterTest.php @@ -0,0 +1,70 @@ +markTestSkipped('There is no SASS_BIN environment variable.'); + } + + $this->filter = new SassFilter($_SERVER['SASS_BIN']); + } + + public function testSass() + { + $input = <<load(); + + $this->filter->setStyle(SassFilter::STYLE_COMPACT); + $this->filter->filterLoad($asset); + + $this->assertEquals("body { color: red; }\n", $asset->getContent(), '->filterLoad() parses the sass'); + } + + public function testScssGuess() + { + $input = <<<'EOF' +$red: #F00; + +.foo { + color: $red; +} + +EOF; + + $expected = '.foo { color: red; }'; + + $asset = new StringAsset($input, array(), null, 'foo.scss'); + $asset->load(); + + $this->filter->setStyle(SassFilter::STYLE_COMPACT); + $this->filter->filterLoad($asset); + + $this->assertEquals(".foo { color: red; }\n", $asset->getContent(), '->filterLoad() detects SCSS based on source path extension'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Sass/ScssFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Sass/ScssFilterTest.php new file mode 100644 index 0000000..a08fe0e --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Sass/ScssFilterTest.php @@ -0,0 +1,44 @@ +markTestSkipped('There is no SASS_BIN environment variable.'); + } + + $asset = new FileAsset(__DIR__.'/../fixtures/sass/main.scss'); + $asset->load(); + + $filter = new ScssFilter($_SERVER['SASS_BIN']); + $filter->setStyle(ScssFilter::STYLE_COMPACT); + $filter->filterLoad($asset); + + $expected = <<assertEquals($expected, $asset->getContent(), '->filterLoad() loads imports'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/SprocketsFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/SprocketsFilterTest.php new file mode 100644 index 0000000..8694dc5 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/SprocketsFilterTest.php @@ -0,0 +1,69 @@ +markTestSkipped('There is no sprockets configuration.'); + } + + $this->assetRoot = sys_get_temp_dir().'/assetic_sprockets'; + if (is_dir($this->assetRoot)) { + $this->cleanup(); + } else { + mkdir($this->assetRoot); + } + } + + protected function tearDown() + { + $this->cleanup(); + } + + private function cleanup() + { + $it = new \RecursiveDirectoryIterator($this->assetRoot); + foreach (new \RecursiveIteratorIterator($it) as $path => $file) { + if (is_file($path)) { + unlink($path); + } + } + } + + public function testFilterLoad() + { + $asset = new FileAsset(__DIR__.'/fixtures/sprockets/main.js'); + $asset->load(); + + $filter = new SprocketsFilter($_SERVER['SPROCKETS_LIB'], $_SERVER['RUBY_BIN']); + $filter->addIncludeDir(__DIR__.'/fixtures/sprockets/lib1'); + $filter->addIncludeDir(__DIR__.'/fixtures/sprockets/lib2'); + $filter->setAssetRoot($this->assetRoot); + $filter->filterLoad($asset); + + $this->assertContains('/* header.js */', $asset->getContent()); + $this->assertContains('/* include.js */', $asset->getContent()); + $this->assertContains('/* footer.js */', $asset->getContent()); + $this->assertFileExists($this->assetRoot.'/images/image.gif'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/StylusFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/StylusFilterTest.php new file mode 100644 index 0000000..03fc729 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/StylusFilterTest.php @@ -0,0 +1,53 @@ +markTestSkipped('No node.js configuration.'); + } + + $this->filter = new StylusFilter($_SERVER['NODE_BIN'], array($_SERVER['NODE_PATH'])); + } + + public function testFilterLoad() + { + $asset = new StringAsset("body\n font 12px Helvetica, Arial, sans-serif\n color black"); + $asset->load(); + + $this->filter->filterLoad($asset); + + $this->assertEquals("body {\n font: 12px Helvetica, Arial, sans-serif;\n color: #000;\n}\n", $asset->getContent(), '->filterLoad() parses the content'); + } + + public function testFilterLoadWithCompression() + { + $asset = new StringAsset("body\n font 12px Helvetica, Arial, sans-serif\n color black;"); + $asset->load(); + + $this->filter->setCompress(true); + $this->filter->filterLoad($asset); + + $this->assertEquals("body{font:12px Helvetica,Arial,sans-serif;color:#000}\n", $asset->getContent(), '->filterLoad() parses the content and compress it'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/UglifyJsFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/UglifyJsFilterTest.php new file mode 100644 index 0000000..bc4b242 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/UglifyJsFilterTest.php @@ -0,0 +1,100 @@ +markTestSkipped('There is no uglifyJs configuration.'); + } + + $this->asset = new FileAsset(__DIR__.'/fixtures/uglifyjs/script.js'); + $this->asset->load(); + + if (isset($_SERVER['NODE_BIN'])) { + $this->filter = new UglifyJsFilter($_SERVER['UGLIFYJS_BIN'], $_SERVER['NODE_BIN']); + } else { + $this->filter = new UglifyJsFilter($_SERVER['UGLIFYJS_BIN']); + } + } + + protected function tearDown() + { + $this->asset = null; + $this->filter = null; + } + + public function testUglify() + { + $this->filter->filterDump($this->asset); + + $expected = <<assertSame($expected, $this->asset->getContent()); + } + + public function testUnsafeUglify() + { + $this->filter->setUnsafe(true); + $this->filter->filterDump($this->asset); + + $expected = <<assertSame($expected, $this->asset->getContent()); + } + + public function testBeautifyUglify() + { + $this->filter->setBeautify(true); + $this->filter->filterDump($this->asset); + + $expected = <<assertSame($expected, $this->asset->getContent()); + } + + public function testNoCopyrightUglify() + { + $this->filter->setNoCopyright(true); + $this->filter->filterDump($this->asset); + + $expected = 'function bar(a){return var2.push(a),a}var foo=new Array(1,2,3,4),bar=Array(a,b,c),var1=new Array(5),var2=new Array(a),foo=function(a){return a};'; + $this->assertSame($expected, $this->asset->getContent()); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Yui/BaseCompressorFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Yui/BaseCompressorFilterTest.php new file mode 100644 index 0000000..39491b1 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Yui/BaseCompressorFilterTest.php @@ -0,0 +1,31 @@ +assertInstanceOf('Assetic\\Filter\\FilterInterface', $filter, 'BaseCompressorFilter implements FilterInterface'); + } +} + +class YuiCompressorFilterForTest extends BaseCompressorFilter +{ + public function filterDump(AssetInterface $asset) + { + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Yui/CssCompressorFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Yui/CssCompressorFilterTest.php new file mode 100644 index 0000000..61f9fd8 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Yui/CssCompressorFilterTest.php @@ -0,0 +1,23 @@ +assertInstanceOf('Assetic\\Filter\\FilterInterface', $filter, 'CssCompressorFilter implements FilterInterface'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Yui/JsCompressorFilterTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Yui/JsCompressorFilterTest.php new file mode 100644 index 0000000..c391837 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/Yui/JsCompressorFilterTest.php @@ -0,0 +1,62 @@ +assertInstanceOf('Assetic\\Filter\\FilterInterface', $filter, 'JsCompressorFilter implements FilterInterface'); + } + + /** + * @group integration + */ + public function testFilterDump() + { + if (!isset($_SERVER['YUI_COMPRESSOR_JAR'])) { + $this->markTestSkipped('There is no YUI_COMPRESSOR_JAR environment variable.'); + } + + $source = <<load(); + + $filter = new JsCompressorFilter($_SERVER['YUI_COMPRESSOR_JAR']); + $filter->filterDump($asset); + + $this->assertEquals($expected, $asset->getContent(), '->filterDump()'); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/compass.sass b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/compass.sass new file mode 100644 index 0000000..dfc99fb --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/compass.sass @@ -0,0 +1,4 @@ +@import "compass/typography/links/hover-link" + +a + @include hover-link diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/partials/_sass.sass b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/partials/_sass.sass new file mode 100644 index 0000000..8593aab --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/partials/_sass.sass @@ -0,0 +1,4 @@ +@import "compass/utilities" + +@mixin mixin-test($fontSize: 1em) + font-size: $fontSize \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/partials/_scss.scss b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/partials/_scss.scss new file mode 100644 index 0000000..896ba5b --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/partials/_scss.scss @@ -0,0 +1,6 @@ +@import "compass/utilities"; + +@mixin mixin-test($fontSize: 1em) +{ + font-size: $fontSize; +} \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/stylesheet.sass b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/stylesheet.sass new file mode 100644 index 0000000..569d84e --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/stylesheet.sass @@ -0,0 +1,4 @@ +@import "partials/sass" + +.test-class + @include mixin-test(2em) \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/stylesheet.scss b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/stylesheet.scss new file mode 100644 index 0000000..461884c --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/compass/stylesheet.scss @@ -0,0 +1,6 @@ +@import "partials/scss"; + +.test-class +{ + @include mixin-test(2em); +} \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssembed/test.css b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssembed/test.css new file mode 100644 index 0000000..370982c --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssembed/test.css @@ -0,0 +1,4 @@ +.test +{ + background: url(../home.png); +} \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/import.css b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/import.css new file mode 100644 index 0000000..e2c77fe --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/import.css @@ -0,0 +1,2 @@ +/* import.css */ +body { color: red; } \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/main.css b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/main.css new file mode 100644 index 0000000..1a90d49 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/main.css @@ -0,0 +1,4 @@ +/* main.css */ +@import "import.css"; +@import url('more/evenmore/deep1.css'); +body { color: black; } \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/more.sass b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/more.sass new file mode 100644 index 0000000..1463b16 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/more.sass @@ -0,0 +1 @@ +/* more.sass */ \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/more/evenmore/bg.gif b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/more/evenmore/bg.gif new file mode 100644 index 0000000..e69de29 diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/more/evenmore/deep1.css b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/more/evenmore/deep1.css new file mode 100644 index 0000000..433b341 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/more/evenmore/deep1.css @@ -0,0 +1,2 @@ +/* more/evenmore/deep1.css */ +@import url(deep2.css); \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/more/evenmore/deep2.css b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/more/evenmore/deep2.css new file mode 100644 index 0000000..645a40e --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/more/evenmore/deep2.css @@ -0,0 +1,4 @@ +/* more/evenmore/deep2.css */ +body { + background: url(bg.gif); +} \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/noncssimport.css b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/noncssimport.css new file mode 100644 index 0000000..2cea30f --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssimport/noncssimport.css @@ -0,0 +1,2 @@ +/* noncssimport.css */ +@import "more.sass"; \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssmin/fonts.css b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssmin/fonts.css new file mode 100644 index 0000000..2d135f6 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssmin/fonts.css @@ -0,0 +1,3 @@ +body { + color: white; +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssmin/main.css b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssmin/main.css new file mode 100644 index 0000000..8fbabc8 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/cssmin/main.css @@ -0,0 +1,5 @@ +@import url("fonts.css"); + +body { + background: black; +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/home.gif b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/home.gif new file mode 100644 index 0000000..edda560 Binary files /dev/null and b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/home.gif differ diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/home.jpg b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/home.jpg new file mode 100644 index 0000000..e0ad369 Binary files /dev/null and b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/home.jpg differ diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/home.png b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/home.png new file mode 100644 index 0000000..bf12e0b Binary files /dev/null and b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/home.png differ diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/jsmin/js.js b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/jsmin/js.js new file mode 100644 index 0000000..76b7431 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/jsmin/js.js @@ -0,0 +1,7 @@ +var a = "abc"; + +// fsfafwe + +;; + var bbb = "u"; + \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/less/_include.less b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/less/_include.less new file mode 100644 index 0000000..8e508d8 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/less/_include.less @@ -0,0 +1 @@ +.foo { color: blue; } diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/less/main.less b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/less/main.less new file mode 100644 index 0000000..86caa7b --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/less/main.less @@ -0,0 +1,3 @@ +@import "_include"; + +.foo { color: red; } diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/packager/app/application.js b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/packager/app/application.js new file mode 100644 index 0000000..c0a775c --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/packager/app/application.js @@ -0,0 +1,11 @@ +/* +--- + +name: App + +requires: [Util/Util] + +... +*/ + +var bar = foo(); diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/packager/lib/package.yml b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/packager/lib/package.yml new file mode 100644 index 0000000..331b341 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/packager/lib/package.yml @@ -0,0 +1,4 @@ +name: "Util" + +sources: + - "util.js" diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/packager/lib/util.js b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/packager/lib/util.js new file mode 100644 index 0000000..bb94a5a --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/packager/lib/util.js @@ -0,0 +1,11 @@ +/* +--- + +name: Util + +provides: [Util] + +... +*/ + +function foo() {} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/packer/example.js b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/packer/example.js new file mode 100644 index 0000000..d80ab5a --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/packer/example.js @@ -0,0 +1,7 @@ +/** + * Example function + */ +var exampleFunction = function(arg1, arg2) { + // Some comment... + alert('exampleFunction called!'); +} \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sass/_include.scss b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sass/_include.scss new file mode 100644 index 0000000..8e508d8 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sass/_include.scss @@ -0,0 +1 @@ +.foo { color: blue; } diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sass/main.scss b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sass/main.scss new file mode 100644 index 0000000..86caa7b --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sass/main.scss @@ -0,0 +1,3 @@ +@import "_include"; + +.foo { color: red; } diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/include.js b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/include.js new file mode 100644 index 0000000..a17b90b --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/include.js @@ -0,0 +1 @@ +/* include.js */ diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/lib1/assets/images/image.gif b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/lib1/assets/images/image.gif new file mode 100755 index 0000000..f32722a Binary files /dev/null and b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/lib1/assets/images/image.gif differ diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/lib1/header.js b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/lib1/header.js new file mode 100644 index 0000000..c60f8d5 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/lib1/header.js @@ -0,0 +1,3 @@ +/* header.js */ + +//= provide "assets" diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/lib2/footer.js b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/lib2/footer.js new file mode 100644 index 0000000..5ce2b9c --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/lib2/footer.js @@ -0,0 +1 @@ +/* footer.js */ diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/main.js b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/main.js new file mode 100644 index 0000000..6ea483a --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/sprockets/main.js @@ -0,0 +1,5 @@ +//= require
    + +//= require "include" + +//= require
    diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/uglifyjs/script.js b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/uglifyjs/script.js new file mode 100644 index 0000000..a7c2233 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Filter/fixtures/uglifyjs/script.js @@ -0,0 +1,18 @@ +/** + * Copyright + */ + +var foo = new Array(1, 2, 3, 4); +var bar = Array(a, b, c); +var var1 = new Array(5); +var var2 = new Array(a); + +function bar(foo) { + var2.push(foo); + return foo; +} + +// comment +var foo = function (var1) { + return var1; +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/FilterManagerTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/FilterManagerTest.php new file mode 100644 index 0000000..6d54a71 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/FilterManagerTest.php @@ -0,0 +1,58 @@ +fm = new FilterManager(); + } + + public function testInvalidName() + { + $this->setExpectedException('InvalidArgumentException'); + + $this->fm->get('foo'); + } + + public function testGetFilter() + { + $filter = $this->getMock('Assetic\\Filter\\FilterInterface'); + $name = 'foo'; + + $this->fm->set($name, $filter); + + $this->assertSame($filter, $this->fm->get($name), '->set() sets a filter'); + } + + public function testHas() + { + $this->fm->set('foo', $this->getMock('Assetic\\Filter\\FilterInterface')); + $this->assertTrue($this->fm->has('foo'), '->has() returns true if the filter is set'); + } + + public function testHasInvalid() + { + $this->assertFalse($this->fm->has('foo'), '->has() returns false if the filter is not set'); + } + + public function testInvalidAlias() + { + $this->setExpectedException('InvalidArgumentException'); + $this->fm->set('@foo', $this->getMock('Assetic\\Filter\\FilterInterface')); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Fixture/messages.de.js b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Fixture/messages.de.js new file mode 100644 index 0000000..d2c7780 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Fixture/messages.de.js @@ -0,0 +1 @@ +var messages = {"text.greeting": "Hallo %name%!"}; \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Fixture/messages.en.js b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Fixture/messages.en.js new file mode 100644 index 0000000..3eb9da2 --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Fixture/messages.en.js @@ -0,0 +1 @@ +var messages = {"text.greeting": "Hello %name%!"}; \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Fixture/messages.fr.js b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Fixture/messages.fr.js new file mode 100644 index 0000000..8fd4c4d --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Fixture/messages.fr.js @@ -0,0 +1 @@ +var messages = {"text.greet": "All\u00f4 %name%!"}; \ No newline at end of file diff --git a/vendor/kriswallsmith/assetic/tests/Assetic/Test/Util/TraversableStringTest.php b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Util/TraversableStringTest.php new file mode 100644 index 0000000..47bd05a --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/Assetic/Test/Util/TraversableStringTest.php @@ -0,0 +1,35 @@ +assertEquals('foo', (string) $foo); + } + + public function testArray() + { + $foo = new TraversableString('foo', array('foo', 'bar')); + + $values = array(); + foreach ($foo as $value) { + $values[] = $value; + } + + $this->assertEquals(array('foo', 'bar'), $values); + } +} diff --git a/vendor/kriswallsmith/assetic/tests/bootstrap.php b/vendor/kriswallsmith/assetic/tests/bootstrap.php new file mode 100644 index 0000000..767818d --- /dev/null +++ b/vendor/kriswallsmith/assetic/tests/bootstrap.php @@ -0,0 +1,46 @@ +add('Assetic\Test', __DIR__); + +if (isset($_SERVER['TWIG_LIB'])) { + $loader->add('Twig_', $_SERVER['TWIG_LIB']); +} + +if (isset($_SERVER['LESSPHP'])) { + require_once $_SERVER['LESSPHP']; +} + +if (isset($_SERVER['CSSMIN'])) { + require_once $_SERVER['CSSMIN']; +} + +if (isset($_SERVER['JSMIN'])) { + require_once $_SERVER['JSMIN']; +} + +if (isset($_SERVER['JSMINPLUS'])) { + require_once $_SERVER['JSMINPLUS']; +} + +if (isset($_SERVER['PACKAGER'])) { + require_once $_SERVER['PACKAGER']; +} + +if (isset($_SERVER['PACKER'])) { + require_once $_SERVER['PACKER']; +} \ No newline at end of file diff --git a/vendor/monolog/monolog/.travis.yml b/vendor/monolog/monolog/.travis.yml new file mode 100644 index 0000000..eb35049 --- /dev/null +++ b/vendor/monolog/monolog/.travis.yml @@ -0,0 +1,11 @@ +language: php + +php: + - 5.3 + - 5.4 + +before_script: + - curl -s http://getcomposer.org/installer | php + - php composer.phar install --dev + +script: phpunit diff --git a/vendor/monolog/monolog/CHANGELOG.mdown b/vendor/monolog/monolog/CHANGELOG.mdown new file mode 100644 index 0000000..ddaa16e --- /dev/null +++ b/vendor/monolog/monolog/CHANGELOG.mdown @@ -0,0 +1,38 @@ +* 1.1.0 (2012-04-23) + + Changes: + + * Added Monolog\Logger::isHandling() to check if a handler will + handle the given log level + * Added ChromePHPHandler + * Added MongoDBHandler + * Added GelfHandler (for use with Graylog2 servers) + * Added SocketHandler (for use with syslog-ng for example) + * Added NormalizerFormatter + * Added the possibility to change the activation strategy of the FingersCrossedHandler + * Added possibility to show microseconds in logs + * Added `server` and `referer` to WebProcessor output + +* 1.0.2 (2011-10-24) + + Changes: + + * Fixed bug in IE with large response headers and FirePHPHandler + +* 1.0.1 (2011-08-25) + + Changes: + + * Added MemoryPeakUsageProcessor and MemoryUsageProcessor + * Added Monolog\Logger::getName() to get a logger's channel name + +* 1.0.0 (2011-07-06) + + Changes: + + * Added IntrospectionProcessor to get info from where the logger was called + * Fixed WebProcessor in CLI + +* 1.0.0-RC1 (2011-07-01) + + * Initial release diff --git a/vendor/monolog/monolog/LICENSE b/vendor/monolog/monolog/LICENSE new file mode 100644 index 0000000..5df1c39 --- /dev/null +++ b/vendor/monolog/monolog/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) Jordi Boggiano + +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/vendor/monolog/monolog/README.mdown b/vendor/monolog/monolog/README.mdown new file mode 100644 index 0000000..786aac1 --- /dev/null +++ b/vendor/monolog/monolog/README.mdown @@ -0,0 +1,172 @@ +Monolog - Logging for PHP 5.3 +============================= + +[![Build Status](https://secure.travis-ci.org/Seldaek/monolog.png)](http://travis-ci.org/Seldaek/monolog) + +Usage +----- + + use Monolog\Logger; + use Monolog\Handler\StreamHandler; + + // create a log channel + $log = new Logger('name'); + $log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING)); + + // add records to the log + $log->addWarning('Foo'); + $log->addError('Bar'); + +Core Concepts +------------- + +Every Logger instance has a channel (name) and a stack of handlers. Whenever +you add a record to the logger, it traverses the handler stack. Each handler +decides whether it handled fully the record, and if so, the propagation of the +record ends there. + +This allow for flexible logging setups, for example having a FileHandler at +the bottom of the stack that will log anything to disk, and on top of that add +a MailHandler that will send emails only when an error message is logged. +Handlers also have a bubbling property which define whether they block the +record or not if they handled it. In this example, setting the MailHandler's +$bubble argument to true means that all records will propagate to the +FileHandler, even the errors that are handled by the MailHandler. + +You can create many Loggers, each defining a channel (e.g.: db, request, +router, ..) and each of them combining various handlers, which can be shared +or not. The channel is reflected in the logs and allows you to easily see or +filter records. + +Each Handler also has a Formatter, a default one with settings that make sense +will be created if you don't set one. The formatters normalize and format +incoming records so that they can be used by the handlers to output useful +information. + +Custom severity levels are not available. Only six levels (debug, info, +warning, error, critical, alert) are present for basic filtering purposes, but +for sorting and other use cases that would require flexibility, you should add +Processors to the Logger that can add extra information (tags, user ip, ..) to +the records before they are handled. + +Log Levels +---------- + +Monolog exposes 6 log levels. Although it is possible to add more by extending +the classes you need, these are generally enough. + +- **DEBUG** (100): Detailed debug information. + +- **INFO** (200): Interesting events. Examples: User logs in, SQL logs. + +- **WARNING** (300): Exceptional occurrences that are not errors. Examples: + Use of deprecated APIs, poor use of an API, undesirable things that are not + necessarily wrong. + +- **ERROR** (400): Runtime errors that do not require immediate action but + should typically be logged and monitored. + +- **CRITICAL** (500): Critical conditions. Example: Application component + unavailable, unexpected exception. + +- **ALERT** (550): Action must be taken immediately. Example: Entire website + down, database unavailable, etc. This should trigger the SMS alerts and wake + you up. + +Docs +==== + +**See the doc/ directory for more detailed documentation. The following is only a list of all parts that come with Monolog.** + +Handlers +-------- + +- _StreamHandler_: Logs records into any php stream, use this for log files. +- _RotatingFileHandler_: Logs records to a file and creates one logfile per day. + It will also delete files older than $maxFiles. You should use + [logrotate](http://linuxcommand.org/man_pages/logrotate8.html) for high profile + setups though, this is just meant as a quick and dirty solution. +- _FirePHPHandler_: Handler for [FirePHP](http://www.firephp.org/), providing + inline `console` messages within [FireBug](http://getfirebug.com/). +- _ChromePHPHandler_: Handler for [ChromePHP](http://www.chromephp.com/), providing + inline `console` messages within Chrome. +- _MongoDBHandler_: Handler to write records in MongoDB via a + [Mongo](http://pecl.php.net/package/mongo) extension connection. +- _NativeMailHandler_: Sends emails using PHP's mail() function. +- _SwiftMailerHandler_: Sends emails using a SwiftMailer instance. +- _SyslogHandler_: Logs records to the syslog. +- _GelfHandler_: Logs records to a [Graylog2](http://www.graylog2.org) server. +- _SocketHandler_: Logs records to [sockets](http://php.net/fsockopen), use this + for UNIX and TCP sockets. See an [example](https://github.com/Seldaek/monolog/blob/master/doc/sockets.md). + +Wrappers / Special Handlers +--------------------------- + +- _FingersCrossedHandler_: A very interesting wrapper. It takes a logger as + parameter and will accumulate log records of all levels until a record + exceeds the defined severity level. At which point it delivers all records, + including those of lower severity, to the handler it wraps. This means that + until an error actually happens you will not see anything in your logs, but + when it happens you will have the full information, including debug and info + records. This provides you with all the information you need, but only when + you need it. +- _NullHandler_: Any record it can handle will be thrown away. This can be used + to put on top of an existing handler stack to disable it temporarily. +- _BufferHandler_: This handler will buffer all the log records it receives + until close() is called at which point it will call handleBatch() on the + handler it wraps with all the log messages at once. This is very useful to + send an email with all records at once for example instead of having one mail + for every log record. +- _GroupHandler_: This handler groups other handlers. Every record received is + sent to all the handlers it is configured with. +- _TestHandler_: Used for testing, it records everything that is sent to it and + has accessors to read out the information. + +Formatters +---------- + +- _LineFormatter_: Formats a log record into a one-line string. +- _NormalizerFormatter_: Normalizes objects/resources down to strings so a record can easily be serialized/encoded. +- _JsonFormatter_: Encodes a log record into json. +- _WildfireFormatter_: Used to format log records into the Wildfire/FirePHP protocol, only useful for the FirePHPHandler. +- _ChromePHPFormatter_: Used to format log records into the ChromePHP format, only useful for the ChromePHPHandler. +- _GelfFormatter_: Used to format log records into Gelf message instances, only useful for the GelfHandler. + +Processors +---------- + +- _IntrospectionProcessor_: Adds the line/file/class/method from which the log call originated. +- _WebProcessor_: Adds the current request URI, request method and client IP to a log record. +- _MemoryUsageProcessor_: Adds the current memory usage to a log record. +- _MemoryPeakUsageProcessor_: Adds the peak memory usage to a log record. + +About +===== + +Requirements +------------ + +- Any flavor of PHP 5.3 should do +- [optional] PHPUnit 3.5+ to execute the test suite (phpunit --version) + +Submitting bugs and feature requests +------------------------------------ + +Bugs and feature request are tracked on [GitHub](https://github.com/Seldaek/monolog/issues) + +Author +------ + +Jordi Boggiano - -
    +See also the list of [contributors](https://github.com/Seldaek/monolog/contributors) which participated in this project. + +License +------- + +Monolog is licensed under the MIT License - see the LICENSE file for details + +Acknowledgements +---------------- + +This library is heavily inspired by Python's [Logbook](http://packages.python.org/Logbook/) +library, although most concepts have been adjusted to fit to the PHP world. diff --git a/vendor/monolog/monolog/composer.json b/vendor/monolog/monolog/composer.json new file mode 100644 index 0000000..c8c28b0 --- /dev/null +++ b/vendor/monolog/monolog/composer.json @@ -0,0 +1,27 @@ +{ + "name": "monolog/monolog", + "description": "Logging for PHP 5.3", + "keywords": ["log","logging"], + "homepage": "http://github.com/Seldaek/monolog", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "mlehner/gelf-php": "1.0.*" + }, + "suggest": { + "mlehner/gelf-php": "Allow sending log messages to a GrayLog2 server" + }, + "autoload": { + "psr-0": {"Monolog": "src/"} + } +} diff --git a/vendor/monolog/monolog/doc/extending.md b/vendor/monolog/monolog/doc/extending.md new file mode 100644 index 0000000..fcd7af2 --- /dev/null +++ b/vendor/monolog/monolog/doc/extending.md @@ -0,0 +1,76 @@ +Extending Monolog +================= + +Monolog is fully extensible, allowing you to adapt your logger to your needs. + +Writing your own handler +------------------------ + +Monolog provides many built-in handlers. But if the one you need does not +exist, you can write it and use it in your logger. The only requirement is +to implement `Monolog\Handler\HandlerInterface`. + +Let's write a PDOHandler to log records to a database. We will extend the +abstract class provided by Monolog to keep things DRY. + +```php +pdo = $pdo; + parent::__construct($level, $bubble); + } + + protected function write(array $record) + { + if (!$this->initialized) { + $this->initialize(); + } + + $this->statement->execute(array( + 'channel' => $record['channel'], + 'level' => $record['level'], + 'message' => $record['formatted'], + 'time' => $record['datetime']->format('U'), + )); + } + + private function initialize() + { + $this->pdo->exec( + 'CREATE TABLE IF NOT EXISTS monolog ' + .'(channel VARCHAR(255), level INTEGER, message LONGTEXT, time INTEGER UNSIGNED)' + ); + $this->statement = $this->pdo->prepare( + 'INSERT INTO monolog (channel, level, message, time) VALUES (:channel, :level, :message, :time)' + ); + + $this->initialized = true; + } +} +``` + +You can now use this handler in your logger: + +```php +pushHandler(new PDOHandler(new PDO('sqlite:logs.sqlite')); + +// You can now use your logger +$logger->addInfo('My logger is now ready'); +``` + +The `Monolog\Handler\AbstractProcessingHandler` class provides most of the +logic needed for the handler, including the use of processors and the formatting +of the record (which is why we use ``$record['formatted']`` instead of ``$record['message']``). diff --git a/vendor/monolog/monolog/doc/sockets.md b/vendor/monolog/monolog/doc/sockets.md new file mode 100644 index 0000000..fad30a9 --- /dev/null +++ b/vendor/monolog/monolog/doc/sockets.md @@ -0,0 +1,37 @@ +Sockets Handler +=============== + +This handler allows you to write your logs to sockets using [fsockopen](http://php.net/fsockopen) +or [pfsockopen](http://php.net/pfsockopen). + +Persistent sockets are mainly useful in web environments where you gain some performance not closing/opening +the connections between requests. + +Basic Example +------------- + +```php +setPersistent(true); + +// Now add the handler +$logger->pushHandler($handler, Logger::DEBUG); + +// You can now use your logger +$logger->addInfo('My logger is now ready'); + +``` + +In this example, using syslog-ng, you should see the log on the log server: + + cweb1 [2012-02-26 00:12:03] my_logger.INFO: My logger is now ready [] [] + diff --git a/vendor/monolog/monolog/doc/usage.md b/vendor/monolog/monolog/doc/usage.md new file mode 100644 index 0000000..a3cfc7f --- /dev/null +++ b/vendor/monolog/monolog/doc/usage.md @@ -0,0 +1,124 @@ +Using Monolog +============= + +Installation +------------ + +Monolog is available on Packagist ([monolog/monolog](http://packagist.org/packages/monolog/monolog)) +and as such installable via [Composer](http://getcomposer.org/). + +If you do not use Composer, you can grab the code from GitHub, and use any +PSR-0 compatible autoloader (e.g. the [Symfony2 ClassLoader component](https://github.com/symfony/ClassLoader)) +to load Monolog classes. + +Configuring a logger +-------------------- + +Here is a basic setup to log to a file and to firephp on the DEBUG level: + +```php +pushHandler(new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG)); +$logger->pushHandler(new FirePHPHandler()); + +// You can now use your logger +$logger->addInfo('My logger is now ready'); +``` + +Let's explain it. The first step is to create the logger instance which will +be used in your code. The argument is a channel name, which is useful when +you use several loggers (see below for more details about it). + +The logger itself does not know how to handle a record. It delegates it to +some handlers. The code above registers two handlers in the stack to allow +handling records in two different ways. + +Note that the FirePHPHandler is called first as it is added on top of the +stack. This allows you to temporarily add a logger with bubbling disabled if +you want to override other configured loggers. + +Adding extra data in the records +-------------------------------- + +Monolog provides two different ways to add extra informations along the simple +textual message. + +### Using the logging context + +The first way is the context, allowing to pass an array of data along the +record: + +```php +addInfo('Adding a new user', array('username' => 'Seldaek')); +``` + +Simple handlers (like the StreamHandler for instance) will simply format +the array to a string but richer handlers can take advantage of the context +(FirePHP is able to display arrays in pretty way for instance). + +### Using processors + +The second way is to add extra data for all records by using a processor. +Processors can be any callable. They will get the record as parameter and +must return it after having eventually changed the `extra` part of it. Let's +write a processor adding some dummy data in the record: + +```php +pushProcessor(function ($record) { + $record['extra']['dummy'] = 'Hello world!'; + + return $record; +}); +``` + +Monolog provides some built-in processors that can be used in your project. +Look at the README file for the list. + +> Tip: processors can also be registered on a specific handler instead of + the logger to apply only for this handler. + +Leveraging channels +------------------- + +Channels are a great way to identify to which part of the application a record +is related. This is useful in big applications (and is leveraged by +MonologBundle in Symfony2). You can then easily grep through log files for +example to filter this or that type of log record. + +Using different loggers with the same handlers allow to identify the logger +that issued the record (through the channel name) by keeping the same handlers +(for instance to use a single log file). + +```php +pushHandler($stream); +$logger->pushHandler($firephp); + +// Create a logger for the security-related stuff with a different channel +$securityLogger = new Logger('security'); +$securityLogger->pushHandler($stream); +$securityLogger->pushHandler($firephp); +``` diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php new file mode 100644 index 0000000..44335b5 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Formats a log message according to the ChromePHP array format + * + * @author Christophe Coevoet + */ +class ChromePHPFormatter implements FormatterInterface +{ + /** + * Translates Monolog log levels to Wildfire levels. + */ + private $logLevels = array( + Logger::DEBUG => 'log', + Logger::INFO => 'info', + Logger::WARNING => 'warn', + Logger::ERROR => 'error', + Logger::CRITICAL => 'error', + Logger::ALERT => 'error', + ); + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + // Retrieve the line and file if set and remove them from the formatted extra + $backtrace = 'unknown'; + if (isset($record['extra']['file']) && isset($record['extra']['line'])) { + $backtrace = $record['extra']['file'].' : '.$record['extra']['line']; + unset($record['extra']['file']); + unset($record['extra']['line']); + } + + $message = array('message' => $record['message']); + if ($record['context']) { + $message['context'] = $record['context']; + } + if ($record['extra']) { + $message['extra'] = $record['extra']; + } + if (count($message) === 1) { + $message = reset($message); + } + + return array( + $record['channel'], + $message, + $backtrace, + $this->logLevels[$record['level']], + ); + } + + public function formatBatch(array $records) + { + $formatted = array(); + + foreach ($records as $record) { + $formatted[] = $this->format($record); + } + + return $formatted; + } +} \ No newline at end of file diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php b/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php new file mode 100644 index 0000000..77891de --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Interface for formatters + * + * @author Jordi Boggiano + */ +interface FormatterInterface +{ + /** + * Formats a log record. + * + * @param array $record A record to format + * @return mixed The formatted record + */ + function format(array $record); + + /** + * Formats a set of log records. + * + * @param array $records A set of records to format + * @return mixed The formatted set of records + */ + function formatBatch(array $records); +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php new file mode 100644 index 0000000..95c17ad --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; +use Gelf\Message; + +/** + * Serializes a log message according to Wildfire's header requirements + * + * @author Matt Lehner + */ +class GelfMessageFormatter extends NormalizerFormatter +{ + /** + * @var string the name of the system for the Gelf log message + */ + protected $systemName; + + /** + * @var string a prefix for 'extra' fields from the Monolog record (optional) + */ + protected $extraPrefix; + + /** + * @var string a prefix for 'context' fields from the Monolog record (optional) + */ + protected $contextPrefix; + + /** + * Translates Monolog log levels to Graylog2 log priorities. + */ + private $logLevels = array( + Logger::DEBUG => LOG_DEBUG, + Logger::INFO => LOG_INFO, + Logger::WARNING => LOG_WARNING, + Logger::ERROR => LOG_ERR, + Logger::CRITICAL => LOG_CRIT, + Logger::ALERT => LOG_ALERT, + ); + + public function __construct($systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_') + { + parent::__construct('U.u'); + + $this->systemName = $systemName ?: gethostname(); + + $this->extraPrefix = $extraPrefix; + $this->contextPrefix = $contextPrefix; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $record = parent::format($record); + $message = new Message(); + $message + ->setTimestamp($record['datetime']) + ->setShortMessage((string) $record['message']) + ->setFacility($record['channel']) + ->setHost($this->systemName) + ->setLine(isset($record['extra']['line']) ? $record['extra']['line'] : null) + ->setFile(isset($record['extra']['file']) ? $record['extra']['file'] : null) + ->setLevel($this->logLevels[$record['level']]); + + // Do not duplicate these values in the additional fields + unset($record['extra']['line']); + unset($record['extra']['file']); + + foreach ($record['extra'] as $key => $val) { + $message->setAdditional($this->extraPrefix . $key, is_scalar($val) ? $val : $this->toJson($val)); + } + + foreach ($record['context'] as $key => $val) { + $message->setAdditional($this->contextPrefix . $key, is_scalar($val) ? $val : $this->toJson($val)); + } + + return $message; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php new file mode 100644 index 0000000..ab20179 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Encodes whatever record data is passed to it as json + * + * This can be useful to log to databases or remote APIs + * + * @author Jordi Boggiano + */ +class JsonFormatter implements FormatterInterface +{ + /** + * {@inheritdoc} + */ + public function format(array $record) + { + return json_encode($record); + } + + /** + * {@inheritdoc} + */ + public function formatBatch(array $records) + { + return json_encode($records); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php new file mode 100644 index 0000000..61d3b8c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Formats incoming records into a one-line string + * + * This is especially useful for logging to files + * + * @author Jordi Boggiano + * @author Christophe Coevoet + */ +class LineFormatter extends NormalizerFormatter +{ + const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"; + + protected $format; + + /** + * @param string $format The format of the message + * @param string $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct($format = null, $dateFormat = null) + { + $this->format = $format ?: static::SIMPLE_FORMAT; + parent::__construct($dateFormat); + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $vars = parent::format($record); + + $output = $this->format; + foreach ($vars['extra'] as $var => $val) { + if (false !== strpos($output, '%extra.'.$var.'%')) { + $output = str_replace('%extra.'.$var.'%', $this->convertToString($val), $output); + unset($vars['extra'][$var]); + } + } + foreach ($vars as $var => $val) { + $output = str_replace('%'.$var.'%', $this->convertToString($val), $output); + } + + return $output; + } + + public function formatBatch(array $records) + { + $message = ''; + foreach ($records as $record) { + $message .= $this->format($record); + } + + return $message; + } + + protected function normalize($data) + { + if (is_bool($data) || is_null($data)) { + return var_export($data, true); + } + + return parent::normalize($data); + } + + protected function convertToString($data) + { + if (null === $data || is_scalar($data)) { + return (string) $data; + } + + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + return json_encode($this->normalize($data), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } + + return stripslashes(json_encode($this->normalize($data))); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php new file mode 100644 index 0000000..09cadb7 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets + * + * @author Jordi Boggiano + */ +class NormalizerFormatter implements FormatterInterface +{ + const SIMPLE_DATE = "Y-m-d H:i:s"; + + protected $dateFormat; + + /** + * @param string $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct($dateFormat = null) + { + $this->dateFormat = $dateFormat ?: static::SIMPLE_DATE; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + return $this->normalize($record); + } + + /** + * {@inheritdoc} + */ + public function formatBatch(array $records) + { + foreach ($records as $key => $record) { + $records[$key] = $this->format($record); + } + + return $records; + } + + protected function normalize($data) + { + if (null === $data || is_scalar($data)) { + return $data; + } + + if (is_array($data) || $data instanceof \Traversable) { + $normalized = array(); + + foreach ($data as $key => $value) { + $normalized[$key] = $this->normalize($value); + } + + return $normalized; + } + + if ($data instanceof \DateTime) { + return $data->format($this->dateFormat); + } + + if (is_resource($data)) { + return '[resource]'; + } + + return sprintf("[object] (%s: %s)", get_class($data), $this->toJson($data)); + } + + protected function toJson($data) + { + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } + + return json_encode($data); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php new file mode 100644 index 0000000..4c393a9 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Serializes a log message according to Wildfire's header requirements + * + * @author Eric Clemmons (@ericclemmons) + * @author Christophe Coevoet + * @author Kirill chEbba Chebunin + */ +class WildfireFormatter implements FormatterInterface +{ + /** + * Translates Monolog log levels to Wildfire levels. + */ + private $logLevels = array( + Logger::DEBUG => 'LOG', + Logger::INFO => 'INFO', + Logger::WARNING => 'WARN', + Logger::ERROR => 'ERROR', + Logger::CRITICAL => 'ERROR', + Logger::ALERT => 'ERROR', + ); + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + // Retrieve the line and file if set and remove them from the formatted extra + $file = $line = ''; + if (isset($record['extra']['file'])) { + $file = $record['extra']['file']; + unset($record['extra']['file']); + } + if (isset($record['extra']['line'])) { + $line = $record['extra']['line']; + unset($record['extra']['line']); + } + + $message = array('message' => $record['message']); + if ($record['context']) { + $message['context'] = $record['context']; + } + if ($record['extra']) { + $message['extra'] = $record['extra']; + } + if (count($message) === 1) { + $message = reset($message); + } + + // Create JSON object describing the appearance of the message in the console + $json = json_encode(array( + array( + 'Type' => $this->logLevels[$record['level']], + 'File' => $file, + 'Line' => $line, + 'Label' => $record['channel'], + ), + $message, + )); + + // The message itself is a serialization of the above JSON object + it's length + return sprintf( + '%s|%s|', + strlen($json), + $json + ); + } + + public function formatBatch(array $records) + { + throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter'); + } +} \ No newline at end of file diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php new file mode 100644 index 0000000..1349c25 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php @@ -0,0 +1,173 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; + +/** + * Base Handler class providing the Handler structure + * + * @author Jordi Boggiano + */ +abstract class AbstractHandler implements HandlerInterface +{ + protected $level = Logger::DEBUG; + protected $bubble = false; + + /** + * @var FormatterInterface + */ + protected $formatter; + protected $processors = array(); + + /** + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($level = Logger::DEBUG, $bubble = true) + { + $this->level = $level; + $this->bubble = $bubble; + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + return $record['level'] >= $this->level; + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + foreach ($records as $record) { + $this->handle($record); + } + } + + /** + * Closes the handler. + * + * This will be called automatically when the object is destroyed + */ + public function close() + { + } + + /** + * {@inheritdoc} + */ + public function pushProcessor($callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given'); + } + array_unshift($this->processors, $callback); + } + + /** + * {@inheritdoc} + */ + public function popProcessor() + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + return array_shift($this->processors); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(FormatterInterface $formatter) + { + $this->formatter = $formatter; + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + if (!$this->formatter) { + $this->formatter = $this->getDefaultFormatter(); + } + + return $this->formatter; + } + + /** + * Sets minimum logging level at which this handler will be triggered. + * + * @param integer $level + */ + public function setLevel($level) + { + $this->level = $level; + } + + /** + * Gets minimum logging level at which this handler will be triggered. + * + * @return integer + */ + public function getLevel() + { + return $this->level; + } + + /** + * Sets the bubbling behavior. + * + * @param Boolean $bubble True means that bubbling is not permitted. + * False means that this handler allows bubbling. + */ + public function setBubble($bubble) + { + $this->bubble = $bubble; + } + + /** + * Gets the bubbling behavior. + * + * @return Boolean True means that bubbling is not permitted. + * False means that this handler allows bubbling. + */ + public function getBubble() + { + return $this->bubble; + } + + public function __destruct() + { + try { + $this->close(); + } catch(\Exception $e) { + // do nothing + } + } + + /** + * Gets the default formatter. + * + * @return FormatterInterface + */ + protected function getDefaultFormatter() + { + return new LineFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php new file mode 100644 index 0000000..9babe03 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; + +/** + * Base Handler class providing the Handler structure + * + * Classes extending it should (in most cases) only implement write($record) + * + * @author Jordi Boggiano + * @author Christophe Coevoet + */ +abstract class AbstractProcessingHandler extends AbstractHandler +{ + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($record['level'] < $this->level) { + return false; + } + + $record = $this->processRecord($record); + + $record['formatted'] = $this->getFormatter()->format($record); + + $this->write($record); + + return false === $this->bubble; + } + + /** + * Writes the record down to the log of the implementing handler + * + * @param array $record + * @return void + */ + abstract protected function write(array $record); + + /** + * Processes a record. + * + * @param array $record + * @return array + */ + protected function processRecord(array $record) + { + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php new file mode 100644 index 0000000..7031607 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Buffers all records until closing the handler and then pass them as batch. + * + * This is useful for a MailHandler to send only one mail per request instead of + * sending one per log message. + * + * @author Christophe Coevoet + */ +class BufferHandler extends AbstractHandler +{ + protected $handler; + protected $bufferSize; + protected $buffer = array(); + + /** + * @param HandlerInterface $handler Handler. + * @param integer $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(HandlerInterface $handler, $bufferSize = 0, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + $this->handler = $handler; + $this->bufferSize = $bufferSize; + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($record['level'] < $this->level) { + return false; + } + + $this->buffer[] = $record; + if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { + array_shift($this->buffer); + } + + return false === $this->bubble; + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->handler->handleBatch($this->buffer); + } +} \ No newline at end of file diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php new file mode 100644 index 0000000..4d7ae8c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\ChromePHPFormatter; + +/** + * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/) + * + * @author Christophe Coevoet + */ +class ChromePHPHandler extends AbstractProcessingHandler +{ + /** + * Version of the extension + */ + const VERSION = '3.0'; + + /** + * Header name + */ + const HEADER_NAME = 'X-ChromePhp-Data'; + + static protected $initialized = false; + + static protected $json = array( + 'version' => self::VERSION, + 'columns' => array('label', 'log', 'backtrace', 'type'), + 'rows' => array(), + ); + + protected $sendHeaders = true; + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + $messages = array(); + + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + $messages[] = $this->processRecord($record); + } + + if (!empty($messages)) { + $messages = $this->getFormatter()->formatBatch($messages); + self::$json['rows'] = array_merge(self::$json['rows'], $messages); + $this->send(); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new ChromePHPFormatter(); + } + + /** + * Creates & sends header for a record + * + * @see sendHeader() + * @see send() + * @param array $record + */ + protected function write(array $record) + { + self::$json['rows'][] = $record['formatted']; + + $this->send(); + } + + /** + * Sends the log header + * + * @see sendHeader() + */ + protected function send() + { + if (!self::$initialized) { + $this->sendHeaders = $this->headersAccepted(); + self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; + + self::$initialized = true; + } + + $this->sendHeader(self::HEADER_NAME, base64_encode(utf8_encode(json_encode(self::$json)))); + } + + /** + * Send header string to the client + * + * @param string $header + * @param string $content + */ + protected function sendHeader($header, $content) + { + if (!headers_sent() && $this->sendHeaders) { + header(sprintf('%s: %s', $header, $content)); + } + } + + /** + * Verifies if the headers are accepted by the current user agent + * + * @return Boolean + */ + protected function headersAccepted() + { + return !isset($_SERVER['HTTP_USER_AGENT']) + || preg_match('{\bChrome/\d+[\.\d+]*\b}', $_SERVER['HTTP_USER_AGENT']); + } +} \ No newline at end of file diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php new file mode 100644 index 0000000..54b9a8d --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +/** + * Interface for activation strategies for the FingersCrossedHandler. + * + * @author Johannes M. Schmitt + */ +interface ActivationStrategyInterface +{ + /** + * Returns whether the given record activates the handler. + * + * @param array $record + * @return Boolean + */ + function isHandlerActivated(array $record); +} \ No newline at end of file diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php new file mode 100644 index 0000000..2cfe6dd --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +/** + * Error level based activation strategy. + * + * @author Johannes M. Schmitt + */ +class ErrorLevelActivationStrategy implements ActivationStrategyInterface +{ + private $actionLevel; + + public function __construct($actionLevel) + { + $this->actionLevel = $actionLevel; + } + + public function isHandlerActivated(array $record) + { + return $record['level'] >= $this->actionLevel; + } +} \ No newline at end of file diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php new file mode 100644 index 0000000..3731fa8 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; +use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; +use Monolog\Logger; + +/** + * Buffers all records until a certain level is reached + * + * The advantage of this approach is that you don't get any clutter in your log files. + * Only requests which actually trigger an error (or whatever your actionLevel is) will be + * in the logs, but they will contain all records, not only those above the level threshold. + * + * @author Jordi Boggiano + */ +class FingersCrossedHandler extends AbstractHandler +{ + protected $handler; + protected $activationStrategy; + protected $buffering = true; + protected $bufferSize; + protected $buffer = array(); + protected $stopBuffering; + + /** + * @param callback|HandlerInterface $handler Handler or factory callback($record, $fingersCrossedHandler). + * @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action + * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param Boolean $stopBuffering Whether the handler should stop buffering after being triggered (default true) + */ + public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true) + { + if (null === $activationStrategy) { + $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING); + } + if (!$activationStrategy instanceof ActivationStrategyInterface) { + $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy); + } + + $this->handler = $handler; + $this->activationStrategy = $activationStrategy; + $this->bufferSize = $bufferSize; + $this->bubble = $bubble; + $this->stopBuffering = $stopBuffering; + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($this->buffering) { + $this->buffer[] = $record; + if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { + array_shift($this->buffer); + } + if ($this->activationStrategy->isHandlerActivated($record)) { + if ($this->stopBuffering) { + $this->buffering = false; + } + if (!$this->handler instanceof HandlerInterface) { + $this->handler = call_user_func($this->handler, $record, $this); + } + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callback should return a HandlerInterface"); + } + $this->handler->handleBatch($this->buffer); + $this->buffer = array(); + } + } else { + $this->handler->handle($record); + } + + return false === $this->bubble; + } + + /** + * Resets the state of the handler. Stops forwarding records to the wrapped handler. + */ + public function reset() + { + $this->buffering = true; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php new file mode 100644 index 0000000..8cc10e9 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php @@ -0,0 +1,161 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\WildfireFormatter; + +/** + * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol. + * + * @author Eric Clemmons (@ericclemmons) + */ +class FirePHPHandler extends AbstractProcessingHandler +{ + /** + * WildFire JSON header message format + */ + const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2'; + + /** + * FirePHP structure for parsing messages & their presentation + */ + const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'; + + /** + * Must reference a "known" plugin, otherwise headers won't display in FirePHP + */ + const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3'; + + /** + * Header prefix for Wildfire to recognize & parse headers + */ + const HEADER_PREFIX = 'X-Wf'; + + /** + * Whether or not Wildfire vendor-specific headers have been generated & sent yet + */ + protected static $initialized = false; + + /** + * Shared static message index between potentially multiple handlers + * @var int + */ + protected static $messageIndex = 1; + + protected $sendHeaders = true; + + /** + * Base header creation function used by init headers & record headers + * + * @param array $meta Wildfire Plugin, Protocol & Structure Indexes + * @param string $message Log message + * @return array Complete header string ready for the client as key and message as value + */ + protected function createHeader(array $meta, $message) + { + $header = sprintf('%s-%s', self::HEADER_PREFIX, join('-', $meta)); + + return array($header => $message); + } + + /** + * Creates message header from record + * + * @see createHeader() + * @param array $record + * @return string + */ + protected function createRecordHeader(array $record) + { + // Wildfire is extensible to support multiple protocols & plugins in a single request, + // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake. + return $this->createHeader( + array(1, 1, 1, self::$messageIndex++), + $record['formatted'] + ); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new WildfireFormatter(); + } + + /** + * Wildfire initialization headers to enable message parsing + * + * @see createHeader() + * @see sendHeader() + * @return array + */ + protected function getInitHeaders() + { + // Initial payload consists of required headers for Wildfire + return array_merge( + $this->createHeader(array('Protocol', 1), self::PROTOCOL_URI), + $this->createHeader(array(1, 'Structure', 1), self::STRUCTURE_URI), + $this->createHeader(array(1, 'Plugin', 1), self::PLUGIN_URI) + ); + } + + /** + * Send header string to the client + * + * @param string $header + * @param string $content + */ + protected function sendHeader($header, $content) + { + if (!headers_sent() && $this->sendHeaders) { + header(sprintf('%s: %s', $header, $content)); + } + } + + /** + * Creates & sends header for a record, ensuring init headers have been sent prior + * + * @see sendHeader() + * @see sendInitHeaders() + * @param array $record + */ + protected function write(array $record) + { + // WildFire-specific headers must be sent prior to any messages + if (!self::$initialized) { + $this->sendHeaders = $this->headersAccepted(); + + foreach ($this->getInitHeaders() as $header => $content) { + $this->sendHeader($header, $content); + } + + self::$initialized = true; + } + + $header = $this->createRecordHeader($record); + $this->sendHeader(key($header), current($header)); + } + + /** + * Verifies if the headers are accepted by the current user agent + * + * @return Boolean + */ + protected function headersAccepted() + { + return !isset($_SERVER['HTTP_USER_AGENT']) + || preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT']) + || isset($_SERVER['HTTP_X_FIREPHP_VERSION']); + } +} \ No newline at end of file diff --git a/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php new file mode 100644 index 0000000..7346029 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Gelf\IMessagePublisher; +use Monolog\Logger; +use Monolog\Handler\AbstractProcessingHandler; +use Monolog\Formatter\GelfMessageFormatter; + +/** + * Handler to send messages to a Graylog2 (http://www.graylog2.org) server + * + * @author Matt Lehner + */ +class GelfHandler extends AbstractProcessingHandler +{ + /** + * @var Gelf\IMessagePublisher the publisher object that sends the message to the server + */ + protected $publisher; + + /** + * @param Gelf\IMessagePublisher $publisher a publisher object + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(IMessagePublisher $publisher, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + + $this->publisher = $publisher; + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->publisher = null; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->publisher->publish($record['formatted']); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new GelfMessageFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php new file mode 100644 index 0000000..c94c52f --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Forwards records to multiple handlers + * + * @author Lenar Lõhmus + */ +class GroupHandler extends AbstractHandler +{ + protected $handlers; + + /** + * @param array $handlers Array of Handlers. + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(array $handlers, $bubble = true) + { + foreach ($handlers as $handler) { + if (!$handler instanceof HandlerInterface) { + throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.'); + } + } + + $this->handlers = $handlers; + $this->bubble = $bubble; + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + foreach ($this->handlers as $handler) { + $handler->handle($record); + } + + return false === $this->bubble; + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + foreach ($this->handlers as $handler) { + $handler->handleBatch($records); + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php b/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php new file mode 100644 index 0000000..24f82d7 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; + +/** + * Interface that all Monolog Handlers must implement + * + * @author Jordi Boggiano + */ +interface HandlerInterface +{ + /** + * Checks whether the given record will be handled by this handler. + * + * This is mostly done for performance reasons, to avoid calling processors for nothing. + * + * @return Boolean + */ + function isHandling(array $record); + + /** + * Handles a record. + * + * The return value of this function controls the bubbling process of the handler stack. + * + * @param array $record The record to handle + * @return Boolean True means that this handler handled the record, and that bubbling is not permitted. + * False means the record was either not processed or that this handler allows bubbling. + */ + function handle(array $record); + + /** + * Handles a set of records at once. + * + * @param array $records The records to handle (an array of record arrays) + */ + function handleBatch(array $records); + + /** + * Adds a processor in the stack. + * + * @param callable $callback + */ + function pushProcessor($callback); + + /** + * Removes the processor on top of the stack and returns it. + * + * @return callable + */ + function popProcessor(); + + /** + * Sets the formatter. + * + * @param FormatterInterface $formatter + */ + function setFormatter(FormatterInterface $formatter); + + /** + * Gets the formatter. + * + * @return FormatterInterface + */ + function getFormatter(); +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php new file mode 100644 index 0000000..94ed841 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Base class for all mail handlers + * + * @author Gyula Sallai + */ +abstract class MailHandler extends AbstractProcessingHandler +{ + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + $messages = array(); + + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + $messages[] = $this->processRecord($record); + } + + if (!empty($messages)) { + $this->send((string) $this->getFormatter()->formatBatch($messages), $messages); + } + } + + /** + * Send a mail with the given content + * + * @param string $content + * @param array $records the array of log records that formed this content + */ + abstract protected function send($content, array $records); + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->send((string) $record['formatted'], array($record)); + } +} \ No newline at end of file diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php new file mode 100644 index 0000000..210bb19 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\NormalizerFormatter; + +/** + * Logs to a MongoDB database. + * + * usage example: + * + * $log = new Logger('application'); + * $mongodb = new MongoDBHandler(new \Mongo("mongodb://localhost:27017"), "logs", "prod"); + * $log->pushHandler($mongodb); + * + * @author Thomas Tourlourat + */ +class MongoDBHandler extends AbstractProcessingHandler +{ + private $mongoCollection; + + public function __construct(\Mongo $mongo, $database, $collection, $level = Logger::DEBUG, $bubble = true) + { + $this->mongoCollection = $mongo->selectCollection($database, $collection); + + parent::__construct($level, $bubble); + } + + protected function write(array $record) + { + $this->mongoCollection->save($record["formatted"]); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new NormalizerFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php new file mode 100644 index 0000000..6933ff6 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * NativeMailerHandler uses the mail() function to send the emails + * + * @author Christophe Coevoet + */ +class NativeMailerHandler extends MailHandler +{ + protected $to; + protected $subject; + protected $headers; + + /** + * @param string $to The receiver of the mail + * @param string $subject The subject of the mail + * @param string $from The sender of the mail + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($to, $subject, $from, $level = Logger::ERROR, $bubble = true) + { + parent::__construct($level, $bubble); + $this->to = $to; + $this->subject = $subject; + $this->headers = sprintf("From: %s\r\nContent-type: text/plain; charset=utf-8\r\n", $from); + } + + /** + * {@inheritdoc} + */ + protected function send($content, array $records) + { + mail($this->to, $this->subject, wordwrap($content, 70), $this->headers); + } +} \ No newline at end of file diff --git a/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php new file mode 100644 index 0000000..7caf4a2 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Blackhole + * + * Any record it can handle will be thrown away. This can be used + * to put on top of an existing stack to override it temporarily. + * + * @author Jordi Boggiano + */ +class NullHandler extends AbstractHandler +{ + /** + * @param integer $level The minimum logging level at which this handler will be triggered + */ + public function __construct($level = Logger::DEBUG) + { + parent::__construct($level, false); + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($record['level'] < $this->level) { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php new file mode 100644 index 0000000..ebecd56 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores logs to files that are rotated every day and a limited number of files are kept. + * + * This rotation is only intended to be used as a workaround. Using logrotate to + * handle the rotation is strongly encouraged when you can use it. + * + * @author Christophe Coevoet + */ +class RotatingFileHandler extends StreamHandler +{ + protected $filename; + protected $maxFiles; + protected $mustRotate; + + /** + * @param string $filename + * @param integer $maxFiles The maximal amount of files to keep (0 means unlimited) + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true) + { + $this->filename = $filename; + $this->maxFiles = (int) $maxFiles; + + $fileInfo = pathinfo($this->filename); + $timedFilename = $fileInfo['dirname'].'/'.$fileInfo['filename'].'-'.date('Y-m-d'); + if (!empty($fileInfo['extension'])) { + $timedFilename .= '.'.$fileInfo['extension']; + } + + // disable rotation upfront if files are unlimited + if (0 === $this->maxFiles) { + $this->mustRotate = false; + } + + parent::__construct($timedFilename, $level, $bubble); + } + + /** + * {@inheritdoc} + */ + public function close() + { + parent::close(); + + if (true === $this->mustRotate) { + $this->rotate(); + } + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + // on the first record written, if the log is new, we should rotate (once per day) + if (null === $this->mustRotate) { + $this->mustRotate = !file_exists($this->url); + } + + parent::write($record); + } + + /** + * Rotates the files. + */ + protected function rotate() + { + $fileInfo = pathinfo($this->filename); + $glob = $fileInfo['dirname'].'/'.$fileInfo['filename'].'-*'; + if (!empty($fileInfo['extension'])) { + $glob .= '.'.$fileInfo['extension']; + } + $iterator = new \GlobIterator($glob); + $count = $iterator->count(); + if ($this->maxFiles >= $count) { + // no files to remove + return; + } + + // Sorting the files by name to remove the older ones + $array = iterator_to_array($iterator); + usort($array, function($a, $b) { + return strcmp($b->getFilename(), $a->getFilename()); + }); + + foreach (array_slice($array, $this->maxFiles) as $file) { + if ($file->isWritable()) { + unlink($file->getRealPath()); + } + } + } +} \ No newline at end of file diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php new file mode 100644 index 0000000..b44fad7 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php @@ -0,0 +1,272 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores to any socket - uses fsockopen() or pfsockopen(). + * + * @author Pablo de Leon Belloc + * @see http://php.net/manual/en/function.fsockopen.php + */ +class SocketHandler extends AbstractProcessingHandler +{ + private $connectionString; + private $connectionTimeout; + private $resource; + private $timeout = 0; + private $persistent = false; + private $errno; + private $errstr; + + /** + * @param string $connectionString Socket connection string + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($connectionString, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + $this->connectionString = $connectionString; + $this->connectionTimeout = (float) ini_get('default_socket_timeout'); + } + + /** + * Connect (if necessary) and write to the socket + * + * @param array $record + * + * @throws \UnexpectedValueException + * @throws \RuntimeException + */ + public function write(array $record) + { + $this->connectIfNotConnected(); + $this->writeToSocket((string) $record['formatted']); + } + + /** + * We will not close a PersistentSocket instance so it can be reused in other requests. + */ + public function close() + { + if (!$this->isPersistent()) { + $this->closeSocket(); + } + } + + /** + * Close socket, if open + */ + public function closeSocket() + { + if (is_resource($this->resource)) { + fclose($this->resource); + $this->resource = null; + } + } + + /** + * Set socket connection to nbe persistent. It only has effect before the connection is initiated. + * + * @param type $boolean + */ + public function setPersistent($boolean) + { + $this->persistent = (boolean) $boolean; + } + + /** + * Set connection timeout. Only has effect before we connect. + * + * @param integer $seconds + * + * @see http://php.net/manual/en/function.fsockopen.php + */ + public function setConnectionTimeout($seconds) + { + $this->validateTimeout($seconds); + $this->connectionTimeout = (float) $seconds; + } + + /** + * Set write timeout. Only has effect before we connect. + * + * @param type $seconds + * + * @see http://php.net/manual/en/function.stream-set-timeout.php + */ + public function setTimeout($seconds) + { + $this->validateTimeout($seconds); + $this->timeout = (int) $seconds; + } + + /** + * Get current connection string + * + * @return string + */ + public function getConnectionString() + { + return $this->connectionString; + } + + /** + * Get persistent setting + * + * @return boolean + */ + public function isPersistent() + { + return $this->persistent; + } + + /** + * Get current connection timeout setting + * + * @return float + */ + public function getConnectionTimeout() + { + return $this->connectionTimeout; + } + + /** + * Get current in-transfer timeout + * + * @return float + */ + public function getTimeout() + { + return $this->timeout; + } + + /** + * Check to see if the socket is currently available. + * + * UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details. + * + * @return boolean + */ + public function isConnected() + { + return is_resource($this->resource) + && !feof($this->resource); // on TCP - other party can close connection. + } + + /** + * Wrapper to allow mocking + */ + protected function pfsockopen() + { + return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + + /** + * Wrapper to allow mocking + */ + protected function fsockopen() + { + return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + + /** + * Wrapper to allow mocking + */ + protected function streamSetTimeout() + { + return stream_set_timeout($this->resource, $this->timeout); + } + + /** + * Wrapper to allow mocking + */ + protected function fwrite($data) + { + return @fwrite($this->resource, $data); + } + + /** + * Wrapper to allow mocking + */ + protected function streamGetMetadata() + { + return stream_get_meta_data($this->resource); + } + + private function validateTimeout($value) + { + $ok = filter_var($value, FILTER_VALIDATE_INT, array('options' => array( + 'min_range' => 0, + ))); + if ($ok === false) { + throw new \InvalidArgumentException("Timeout must be 0 or a positive integer (got $value)"); + } + } + + private function connectIfNotConnected() + { + if ($this->isConnected()) { + return; + } + $this->connect(); + } + + private function connect() + { + $this->createSocketResource(); + $this->setSocketTimeout(); + } + + private function createSocketResource() + { + if ($this->isPersistent()) { + $resource = $this->pfsockopen(); + } else { + $resource = $this->fsockopen(); + } + if (!$resource) { + throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)"); + } + $this->resource = $resource; + } + + private function setSocketTimeout() + { + if (!$this->streamSetTimeout()) { + throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()"); + } + } + + private function writeToSocket($data) + { + $length = strlen($data); + $sent = 0; + while ($this->isConnected() && $sent < $length) { + $chunk = $this->fwrite(substr($data, $sent)); + if ($chunk === false) { + throw new \RuntimeException("Could not write to socket"); + } + $sent += $chunk; + $socketInfo = $this->streamGetMetadata(); + if ($socketInfo['timed_out']) { + throw new \RuntimeException("Write timed-out"); + } + } + if (!$this->isConnected() && $sent < $length) { + throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)"); + } + } + +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php new file mode 100644 index 0000000..5593054 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores to any stream resource + * + * Can be used to store into php://stderr, remote and local files, etc. + * + * @author Jordi Boggiano + */ +class StreamHandler extends AbstractProcessingHandler +{ + protected $stream; + protected $url; + + /** + * @param string $stream + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($stream, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + if (is_resource($stream)) { + $this->stream = $stream; + } else { + $this->url = $stream; + } + } + + /** + * {@inheritdoc} + */ + public function close() + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->stream = null; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + if (null === $this->stream) { + if (!$this->url) { + throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().'); + } + $this->stream = @fopen($this->url, 'a'); + if (!is_resource($this->stream)) { + $this->stream = null; + throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened; it may be invalid or not writable.', $this->url)); + } + } + fwrite($this->stream, (string) $record['formatted']); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php new file mode 100644 index 0000000..addc4c4 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * SwiftMailerHandler uses Swift_Mailer to send the emails + * + * @author Gyula Sallai + */ +class SwiftMailerHandler extends MailHandler +{ + protected $mailer; + protected $message; + + /** + * @param \Swift_Mailer $mailer The mailer to use + * @param callback|\Swift_Message $message An example message for real messages, only the body will be replaced + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, $bubble = true) + { + parent::__construct($level, $bubble); + $this->mailer = $mailer; + if (!$message instanceof \Swift_Message && is_callable($message)) { + $message = call_user_func($message); + } + if (!$message instanceof \Swift_Message) { + throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callback returning it'); + } + $this->message = $message; + } + + /** + * {@inheritdoc} + */ + protected function send($content, array $records) + { + $message = clone $this->message; + $message->setBody($content); + + $this->mailer->send($message); + } +} \ No newline at end of file diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php new file mode 100644 index 0000000..444a592 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Logs to syslog service. + * + * usage example: + * + * $log = new Logger('application'); + * $syslog = new SyslogHandler('myfacility', 'local6'); + * $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%"); + * $syslog->setFormatter($formatter); + * $log->pushHandler($syslog); + * + * @author Sven Paulus + */ +class SyslogHandler extends AbstractProcessingHandler +{ + /** + * Translates Monolog log levels to syslog log priorities. + */ + private $logLevels = array( + Logger::DEBUG => LOG_DEBUG, + Logger::INFO => LOG_INFO, + Logger::WARNING => LOG_WARNING, + Logger::ERROR => LOG_ERR, + Logger::CRITICAL => LOG_CRIT, + Logger::ALERT => LOG_ALERT, + ); + + /** + * List of valid log facility names. + */ + private $facilities = array( + 'auth' => LOG_AUTH, + 'authpriv' => LOG_AUTHPRIV, + 'cron' => LOG_CRON, + 'daemon' => LOG_DAEMON, + 'kern' => LOG_KERN, + 'lpr' => LOG_LPR, + 'mail' => LOG_MAIL, + 'news' => LOG_NEWS, + 'syslog' => LOG_SYSLOG, + 'user' => LOG_USER, + 'uucp' => LOG_UUCP, + ); + + /** + * @param string $ident + * @param mixed $facility + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($ident, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->facilities['local0'] = LOG_LOCAL0; + $this->facilities['local1'] = LOG_LOCAL1; + $this->facilities['local2'] = LOG_LOCAL2; + $this->facilities['local3'] = LOG_LOCAL3; + $this->facilities['local4'] = LOG_LOCAL4; + $this->facilities['local5'] = LOG_LOCAL5; + $this->facilities['local6'] = LOG_LOCAL6; + $this->facilities['local7'] = LOG_LOCAL7; + } + + // convert textual description of facility to syslog constant + if (array_key_exists(strtolower($facility), $this->facilities)) { + $facility = $this->facilities[strtolower($facility)]; + } else if (!in_array($facility, array_values($this->facilities), true)) { + throw new \UnexpectedValueException('Unknown facility value "'.$facility.'" given'); + } + + if (!openlog($ident, LOG_PID, $facility)) { + throw new \LogicException('Can\'t open syslog for ident "'.$ident.'" and facility "'.$facility.'"'); + } + } + + /** + * {@inheritdoc} + */ + public function close() + { + closelog(); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + syslog($this->logLevels[$record['level']], (string) $record['formatted']); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php new file mode 100644 index 0000000..8f47855 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Used for testing purposes. + * + * It records all records and gives you access to them for verification. + * + * @author Jordi Boggiano + */ +class TestHandler extends AbstractProcessingHandler +{ + protected $records = array(); + protected $recordsByLevel = array(); + + public function getRecords() + { + return $this->records; + } + + public function hasAlert($record) + { + return $this->hasRecord($record, Logger::ALERT); + } + + public function hasCritical($record) + { + return $this->hasRecord($record, Logger::CRITICAL); + } + + public function hasError($record) + { + return $this->hasRecord($record, Logger::ERROR); + } + + public function hasWarning($record) + { + return $this->hasRecord($record, Logger::WARNING); + } + + public function hasInfo($record) + { + return $this->hasRecord($record, Logger::INFO); + } + + public function hasDebug($record) + { + return $this->hasRecord($record, Logger::DEBUG); + } + + public function hasAlertRecords() + { + return isset($this->recordsByLevel[Logger::ALERT]); + } + + public function hasCriticalRecords() + { + return isset($this->recordsByLevel[Logger::CRITICAL]); + } + + public function hasErrorRecords() + { + return isset($this->recordsByLevel[Logger::ERROR]); + } + + public function hasWarningRecords() + { + return isset($this->recordsByLevel[Logger::WARNING]); + } + + public function hasInfoRecords() + { + return isset($this->recordsByLevel[Logger::INFO]); + } + + public function hasDebugRecords() + { + return isset($this->recordsByLevel[Logger::DEBUG]); + } + + protected function hasRecord($record, $level) + { + if (!isset($this->recordsByLevel[$level])) { + return false; + } + + if (is_array($record)) { + $record = $record['message']; + } + + foreach ($this->recordsByLevel[$level] as $rec) { + if ($rec['message'] === $record) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->recordsByLevel[$record['level']][] = $record; + $this->records[] = $record; + } +} \ No newline at end of file diff --git a/vendor/monolog/monolog/src/Monolog/Logger.php b/vendor/monolog/monolog/src/Monolog/Logger.php new file mode 100644 index 0000000..91a6ca4 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Logger.php @@ -0,0 +1,421 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use Monolog\Handler\HandlerInterface; +use Monolog\Handler\StreamHandler; + +/** + * Monolog log channel + * + * It contains a stack of Handlers and a stack of Processors, + * and uses them to store records that are added to it. + * + * @author Jordi Boggiano + */ +class Logger +{ + /** + * Detailed debug information + */ + const DEBUG = 100; + + /** + * Interesting events + * + * Examples: User logs in, SQL logs. + */ + const INFO = 200; + + /** + * Exceptional occurrences that are not errors + * + * Examples: Use of deprecated APIs, poor use of an API, + * undesirable things that are not necessarily wrong. + */ + const WARNING = 300; + + /** + * Runtime errors + */ + const ERROR = 400; + + /** + * Critical conditions + * + * Example: Application component unavailable, unexpected exception. + */ + const CRITICAL = 500; + + /** + * Action must be taken immediately + * + * Example: Entire website down, database unavailable, etc. + * This should trigger the SMS alerts and wake you up. + */ + const ALERT = 550; + + protected static $levels = array( + 100 => 'DEBUG', + 200 => 'INFO', + 300 => 'WARNING', + 400 => 'ERROR', + 500 => 'CRITICAL', + 550 => 'ALERT', + ); + + protected $name; + + /** + * The handler stack + * + * @var array of Monolog\Handler\HandlerInterface + */ + protected $handlers = array(); + + protected $processors = array(); + + /** + * @param string $name The logging channel + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Pushes a handler on to the stack. + * + * @param HandlerInterface $handler + */ + public function pushHandler(HandlerInterface $handler) + { + array_unshift($this->handlers, $handler); + } + + /** + * Pops a handler from the stack + * + * @return HandlerInterface + */ + public function popHandler() + { + if (!$this->handlers) { + throw new \LogicException('You tried to pop from an empty handler stack.'); + } + return array_shift($this->handlers); + } + + /** + * Adds a processor on to the stack. + * + * @param callable $callback + */ + public function pushProcessor($callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given'); + } + array_unshift($this->processors, $callback); + } + + /** + * Removes the processor on top of the stack and returns it. + * + * @return callable + */ + public function popProcessor() + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + return array_shift($this->processors); + } + + /** + * Adds a log record. + * + * @param integer $level The logging level + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addRecord($level, $message, array $context = array()) + { + if (!$this->handlers) { + $this->pushHandler(new StreamHandler('php://stderr', self::DEBUG)); + } + $record = array( + 'message' => (string) $message, + 'context' => $context, + 'level' => $level, + 'level_name' => self::getLevelName($level), + 'channel' => $this->name, + 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true))), + 'extra' => array(), + ); + // check if any message will handle this message + $handlerKey = null; + foreach ($this->handlers as $key => $handler) { + if ($handler->isHandling($record)) { + $handlerKey = $key; + break; + } + } + // none found + if (null === $handlerKey) { + return false; + } + // found at least one, process message and dispatch it + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + while (isset($this->handlers[$handlerKey]) && + false === $this->handlers[$handlerKey]->handle($record)) { + $handlerKey++; + } + + return true; + } + + /** + * Adds a log record at the DEBUG level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addDebug($message, array $context = array()) + { + return $this->addRecord(self::DEBUG, $message, $context); + } + + /** + * Adds a log record at the INFO level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addInfo($message, array $context = array()) + { + return $this->addRecord(self::INFO, $message, $context); + } + + /** + * Adds a log record at the WARNING level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addWarning($message, array $context = array()) + { + return $this->addRecord(self::WARNING, $message, $context); + } + + /** + * Adds a log record at the ERROR level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addError($message, array $context = array()) + { + return $this->addRecord(self::ERROR, $message, $context); + } + + /** + * Adds a log record at the CRITICAL level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addCritical($message, array $context = array()) + { + return $this->addRecord(self::CRITICAL, $message, $context); + } + + /** + * Adds a log record at the ALERT level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addAlert($message, array $context = array()) + { + return $this->addRecord(self::ALERT, $message, $context); + } + + /** + * Gets the name of the logging level. + * + * @param integer $level + * @return string + */ + public static function getLevelName($level) + { + return self::$levels[$level]; + } + + /** + * Checks whether the Logger has a handler that listens on the given level + * + * @param integer $level + * @return Boolean + */ + public function isHandling($level) + { + $record = array( + 'message' => '', + 'context' => array(), + 'level' => $level, + 'level_name' => self::getLevelName($level), + 'channel' => $this->name, + 'datetime' => new \DateTime(), + 'extra' => array(), + ); + + foreach ($this->handlers as $key => $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + + return false; + } + + // ZF Logger Compat + + /** + * Adds a log record at the DEBUG level. + * + * This method allows to have an easy ZF compatibility. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function debug($message, array $context = array()) + { + return $this->addRecord(self::DEBUG, $message, $context); + } + + /** + * Adds a log record at the INFO level. + * + * This method allows to have an easy ZF compatibility. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function info($message, array $context = array()) + { + return $this->addRecord(self::INFO, $message, $context); + } + + /** + * Adds a log record at the INFO level. + * + * This method allows to have an easy ZF compatibility. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function notice($message, array $context = array()) + { + return $this->addRecord(self::INFO, $message, $context); + } + + /** + * Adds a log record at the WARNING level. + * + * This method allows to have an easy ZF compatibility. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function warn($message, array $context = array()) + { + return $this->addRecord(self::WARNING, $message, $context); + } + + /** + * Adds a log record at the ERROR level. + * + * This method allows to have an easy ZF compatibility. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function err($message, array $context = array()) + { + return $this->addRecord(self::ERROR, $message, $context); + } + + /** + * Adds a log record at the CRITICAL level. + * + * This method allows to have an easy ZF compatibility. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function crit($message, array $context = array()) + { + return $this->addRecord(self::CRITICAL, $message, $context); + } + + /** + * Adds a log record at the ALERT level. + * + * This method allows to have an easy ZF compatibility. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function alert($message, array $context = array()) + { + return $this->addRecord(self::ALERT, $message, $context); + } + + /** + * Adds a log record at the ALERT level. + * + * This method allows to have an easy ZF compatibility. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function emerg($message, array $context = array()) + { + return $this->addRecord(self::ALERT, $message, $context); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php new file mode 100644 index 0000000..f03e347 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects line/file:class/function where the log message came from + * + * Warning: This only works if the handler processes the logs directly. + * If you put the processor on a handler that is behind a FingersCrossedHandler + * for example, the processor will only be called once the trigger level is reached, + * and all the log records will have the same file/line/.. data from the call that + * triggered the FingersCrossedHandler. + * + * @author Jordi Boggiano + */ +class IntrospectionProcessor +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + $trace = debug_backtrace(); + + // skip first since it's always the current method + array_shift($trace); + // the call_user_func call is also skipped + array_shift($trace); + + $i = 0; + while (isset($trace[$i]['class']) && false !== strpos($trace[$i]['class'], 'Monolog\\')) { + $i++; + } + + // we should have the call source now + $record['extra'] = array_merge( + $record['extra'], + array( + 'file' => isset($trace[$i-1]['file']) ? $trace[$i-1]['file'] : null, + 'line' => isset($trace[$i-1]['line']) ? $trace[$i-1]['line'] : null, + 'class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : null, + 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null, + ) + ); + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php new file mode 100644 index 0000000..77e0324 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects memory_get_peak_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryPeakUsageProcessor extends MemoryProcessor +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + $bytes = memory_get_peak_usage($this->realUsage); + $formatted = self::formatBytes($bytes); + + $record['extra'] = array_merge( + $record['extra'], + array( + 'memory_peak_usage' => $formatted, + ) + ); + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php new file mode 100644 index 0000000..7a41238 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Some methods that are common for all memory processors + * + * @author Rob Jensen + */ +abstract class MemoryProcessor +{ + protected $realUsage; + + /** + * @param boolean $realUsage + */ + public function __construct($realUsage = true) + { + $this->realUsage = (boolean) $realUsage; + } + + /** + * Formats bytes into a human readable string + * + * @param int $bytes + * @return string + */ + protected static function formatBytes($bytes) + { + $bytes = (int) $bytes; + + if ($bytes > 1024*1024) { + return round($bytes/1024/1024, 2).' MB'; + } elseif ($bytes > 1024) { + return round($bytes/1024, 2).' KB'; + } + + return $bytes . ' B'; + } + +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php new file mode 100644 index 0000000..0867459 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects memory_get_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryUsageProcessor extends MemoryProcessor +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + $bytes = memory_get_usage($this->realUsage); + $formatted = self::formatBytes($bytes); + + $record['extra'] = array_merge( + $record['extra'], + array( + 'memory_usage' => $formatted, + ) + ); + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php new file mode 100644 index 0000000..61000a0 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects url/method and remote IP of the current web request in all records + * + * @author Jordi Boggiano + */ +class WebProcessor +{ + protected $serverData; + + /** + * @param mixed $serverData array or object w/ ArrayAccess that provides access to the $_SERVER data + */ + public function __construct($serverData = null) + { + if (null === $serverData) { + $this->serverData =& $_SERVER; + } elseif (is_array($serverData) || $serverData instanceof \ArrayAccess) { + $this->serverData = $serverData; + } else { + throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.'); + } + } + + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + // skip processing if for some reason request data + // is not present (CLI or wonky SAPIs) + if (!isset($this->serverData['REQUEST_URI'])) { + return $record; + } + + if (!isset($this->serverData['HTTP_REFERER'])) { + $this->serverData['HTTP_REFERER'] = null; + } + + $record['extra'] = array_merge( + $record['extra'], + array( + 'url' => $this->serverData['REQUEST_URI'], + 'ip' => $this->serverData['REMOTE_ADDR'], + 'http_method' => $this->serverData['REQUEST_METHOD'], + 'server' => $this->serverData['SERVER_NAME'], + 'referrer' => $this->serverData['HTTP_REFERER'], + ) + ); + + return $record; + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Composer/ScriptHandler.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Composer/ScriptHandler.php new file mode 100644 index 0000000..f18bddc --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Composer/ScriptHandler.php @@ -0,0 +1,144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\DistributionBundle\Composer; + +use Symfony\Component\ClassLoader\ClassCollectionLoader; +use Symfony\Component\Process\Process; +use Symfony\Component\Process\PhpExecutableFinder; + +/** + * @author Jordi Boggiano + */ +class ScriptHandler +{ + public static function buildBootstrap($event) + { + $options = self::getOptions($event); + $appDir = $options['symfony-app-dir']; + + if (!is_dir($appDir)) { + echo 'The symfony-app-dir ('.$appDir.') specified in composer.json was not found in '.getcwd().', can not build bootstrap file.'.PHP_EOL; + + return; + } + + static::executeBuildBootstrap($appDir); + } + + public static function clearCache($event) + { + $options = self::getOptions($event); + $appDir = $options['symfony-app-dir']; + + if (!is_dir($appDir)) { + echo 'The symfony-app-dir ('.$appDir.') specified in composer.json was not found in '.getcwd().', can not clear the cache.'.PHP_EOL; + + return; + } + + static::executeCommand($event, $appDir, 'cache:clear --no-warmup'); + } + + public static function installAssets($event) + { + $options = self::getOptions($event); + $appDir = $options['symfony-app-dir']; + $webDir = $options['symfony-web-dir']; + + $symlink = ''; + if ($options['symfony-assets-install'] == 'symlink') { + $symlink = '--symlink '; + } elseif ($options['symfony-assets-install'] == 'relative') { + $symlink = '--symlink --relative '; + } + + if (!is_dir($webDir)) { + echo 'The symfony-web-dir ('.$webDir.') specified in composer.json was not found in '.getcwd().', can not install assets.'.PHP_EOL; + + return; + } + + static::executeCommand($event, $appDir, 'assets:install '.$symlink.escapeshellarg($webDir)); + } + + public static function doBuildBootstrap($appDir) + { + $file = $appDir.'/bootstrap.php.cache'; + if (file_exists($file)) { + unlink($file); + } + + ClassCollectionLoader::load(array( + 'Symfony\\Component\\DependencyInjection\\ContainerAwareInterface', + // Cannot be included because annotations will parse the big compiled class file + //'Symfony\\Component\\DependencyInjection\\ContainerAware', + 'Symfony\\Component\\DependencyInjection\\ContainerInterface', + 'Symfony\\Component\\DependencyInjection\\Container', + 'Symfony\\Component\\HttpKernel\\HttpKernelInterface', + 'Symfony\\Component\\HttpKernel\\KernelInterface', + 'Symfony\\Component\\HttpKernel\\Kernel', + 'Symfony\\Component\\ClassLoader\\ClassCollectionLoader', + 'Symfony\\Component\\ClassLoader\\ApcClassLoader', + 'Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface', + 'Symfony\\Component\\HttpKernel\\Bundle\\Bundle', + 'Symfony\\Component\\Config\\ConfigCache', + // cannot be included as commands are discovered based on the path to this class via Reflection + //'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle', + ), dirname($file), basename($file, '.php.cache'), false, false, '.php.cache'); + + file_put_contents($file, sprintf("find()); + $console = escapeshellarg($appDir.'/console'); + if ($event->getIO()->isDecorated()) { + $console.= ' --ansi'; + } + + $process = new Process($php.' '.$console.' '.$cmd); + $process->run(function ($type, $buffer) { echo $buffer; }); + } + + protected static function executeBuildBootstrap($appDir) + { + $phpFinder = new PhpExecutableFinder; + $php = escapeshellarg($phpFinder->find()); + $cmd = escapeshellarg(__DIR__.'/../Resources/bin/build_bootstrap.php'); + $appDir = escapeshellarg($appDir); + + $process = new Process($php.' '.$cmd.' '.$appDir); + $process->run(function ($type, $buffer) { echo $buffer; }); + } + + protected static function getOptions($event) + { + $options = array_merge(array( + 'symfony-app-dir' => 'app', + 'symfony-web-dir' => 'web', + 'symfony-assets-install' => 'hard' + ), $event->getComposer()->getPackage()->getExtra()); + + $options['symfony-assets-install'] = getenv('SYMFONY_ASSETS_INSTALL') ?: $options['symfony-assets-install']; + + return $options; + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Configurator.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Configurator.php new file mode 100644 index 0000000..8138398 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Configurator.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\DistributionBundle\Configurator; + +use Sensio\Bundle\DistributionBundle\Configurator\Step\StepInterface; +use Symfony\Component\Yaml\Yaml; + +/** + * Configurator. + * + * @author Marc Weistroff + */ +class Configurator +{ + protected $filename; + protected $steps; + protected $parameters; + + public function __construct($kernelDir) + { + $this->kernelDir = $kernelDir; + $this->filename = $kernelDir.'/config/parameters.yml'; + + $this->steps = array(); + $this->parameters = $this->read(); + } + + public function isFileWritable() + { + return is_writable($this->filename); + } + + public function clean() + { + if (file_exists($this->getCacheFilename())) { + @unlink($this->getCacheFilename()); + } + } + + /** + * @param StepInterface $step + */ + public function addStep(StepInterface $step) + { + $this->steps[] = $step; + } + + /** + * @param integer $index + * + * @return StepInterface + */ + public function getStep($index) + { + if (isset($this->steps[$index])) { + return $this->steps[$index]; + } + } + + /** + * @return array + */ + public function getSteps() + { + return $this->steps; + } + + /** + * @return array + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * @return integer + */ + public function getStepCount() + { + return count($this->steps); + } + + /** + * @param array $parameters + */ + public function mergeParameters($parameters) + { + $this->parameters = array_merge($this->parameters, $parameters); + } + + /** + * @return array + */ + public function getRequirements() + { + $majors = array(); + foreach ($this->steps as $step) { + foreach ($step->checkRequirements() as $major) { + $majors[] = $major; + } + } + + return $majors; + } + + /** + * @return array + */ + public function getOptionalSettings() + { + $minors = array(); + foreach ($this->steps as $step) { + foreach ($step->checkOptionalSettings() as $minor) { + $minors[] = $minor; + } + } + + return $minors; + } + + /** + * Renders parameters as a string. + * + * @return string + */ + public function render() + { + return Yaml::dump(array('parameters' => $this->parameters)); + } + + /** + * Writes parameters to parameters.yml or temporary in the cache directory. + * + * @return boolean + */ + public function write() + { + $filename = $this->isFileWritable() ? $this->filename : $this->getCacheFilename(); + + return file_put_contents($filename, $this->render()); + } + + /** + * Reads parameters from file. + * + * @return array + */ + protected function read() + { + $filename = $this->filename; + if (!$this->isFileWritable() && file_exists($this->getCacheFilename())) { + $filename = $this->getCacheFilename(); + } + + $ret = Yaml::parse($filename); + if (false === $ret || array() === $ret) { + throw new \InvalidArgumentException(sprintf('The %s file is not valid.', $filename)); + } + + if (isset($ret['parameters']) && is_array($ret['parameters'])) { + return $ret['parameters']; + } else { + return array(); + } + } + + /** + * getCacheFilename + * + * @return string + */ + protected function getCacheFilename() + { + return $this->kernelDir.'/cache/parameters.yml'; + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Form/DoctrineStepType.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Form/DoctrineStepType.php new file mode 100644 index 0000000..fa315c5 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Form/DoctrineStepType.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\DistributionBundle\Configurator\Form; + +use Sensio\Bundle\DistributionBundle\Configurator\Step\DoctrineStep; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; + +/** + * Doctrine Form Type. + * + * @author Fabien Potencier + */ +class DoctrineStepType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->add('driver', 'choice', array('choices' => DoctrineStep::getDrivers())) + ->add('name', 'text') + ->add('host', 'text') + ->add('port', 'text', array('required' => false)) + ->add('user', 'text') + ->add('password', 'repeated', array( + 'required' => false, + 'type' => 'password', + 'first_name' => 'password', + 'second_name' => 'password_again', + 'invalid_message' => 'The password fields must match.', + )) + ; + } + + public function getName() + { + return 'distributionbundle_doctrine_step'; + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Form/SecretStepType.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Form/SecretStepType.php new file mode 100644 index 0000000..c62ebd1 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Form/SecretStepType.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\DistributionBundle\Configurator\Form; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; + +/** + * Secret Form Type. + * + * @author Marc Weistroff + */ +class SecretStepType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->add('secret', 'text'); + } + + public function getName() + { + return 'distributionbundle_secret_step'; + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/DoctrineStep.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/DoctrineStep.php new file mode 100644 index 0000000..ba2163e --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/DoctrineStep.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\DistributionBundle\Configurator\Step; + +use Sensio\Bundle\DistributionBundle\Configurator\Form\DoctrineStepType; +use Symfony\Component\Validator\Constraints as Assert; + +/** + * Doctrine Step. + * + * @author Fabien Potencier + */ +class DoctrineStep implements StepInterface +{ + /** + * @Assert\Choice(callback="getDriverKeys") + */ + public $driver; + + /** + * @Assert\NotBlank + */ + public $host; + + /** + * @Assert\Min(0) + */ + public $port; + + /** + * @Assert\NotBlank + */ + public $name; + + /** + * @Assert\NotBlank + */ + public $user; + + public $password; + + public function __construct(array $parameters) + { + foreach ($parameters as $key => $value) { + if (0 === strpos($key, 'database_')) { + $parameters[substr($key, 9)] = $value; + $key = substr($key, 9); + $this->$key = $value; + } + } + } + + /** + * @see StepInterface + */ + public function getFormType() + { + return new DoctrineStepType(); + } + + /** + * @see StepInterface + */ + public function checkRequirements() + { + $messages = array(); + + if (!class_exists('\PDO')) { + $messages[] = 'PDO extension is mandatory.'; + } else { + $drivers = \PDO::getAvailableDrivers(); + if (0 == count($drivers)) { + $messages[] = 'Please install PDO drivers.'; + } + } + + return $messages; + } + + /** + * @see StepInterface + */ + public function checkOptionalSettings() + { + return array(); + } + + /** + * @see StepInterface + */ + public function update(StepInterface $data) + { + $parameters = array(); + + foreach ($data as $key => $value) { + $parameters['database_'.$key] = $value; + } + + return $parameters; + } + + /** + * @see StepInterface + */ + public function getTemplate() + { + return 'SensioDistributionBundle:Configurator/Step:doctrine.html.twig'; + } + + /** + * @return array + */ + static public function getDriverKeys() + { + return array_keys(static::getDrivers()); + } + + /** + * @return array + */ + static public function getDrivers() + { + return array( + 'pdo_mysql' => 'MySQL (PDO)', + 'pdo_sqlite' => 'SQLite (PDO)', + 'pdo_pgsql' => 'PosgreSQL (PDO)', + 'oci8' => 'Oracle (native)', + 'ibm_db2' => 'IBM DB2 (native)', + 'pdo_oci' => 'Oracle (PDO)', + 'pdo_ibm' => 'IBM DB2 (PDO)', + 'pdo_sqlsrv' => 'SQLServer (PDO)', + ); + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/SecretStep.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/SecretStep.php new file mode 100644 index 0000000..d2333ba --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/SecretStep.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\DistributionBundle\Configurator\Step; + +use Sensio\Bundle\DistributionBundle\Configurator\Form\SecretStepType; +use Symfony\Component\Validator\Constraints as Assert; + +/** + * Secret Step. + * + * @author Fabien Potencier + */ +class SecretStep implements StepInterface +{ + /** + * @Assert\NotBlank + */ + public $secret; + + public function __construct(array $parameters) + { + if (array_key_exists('secret', $parameters)){ + $this->secret = $parameters['secret']; + + if ('ThisTokenIsNotSoSecretChangeIt' == $this->secret) { + $this->secret = $this->generateRandomSecret(); + } + } else { + $this->secret = $this->generateRandomSecret(); + } + + } + + private function generateRandomSecret() + { + return hash('sha1', uniqid(mt_rand())); + } + + /** + * @see StepInterface + */ + public function getFormType() + { + return new SecretStepType(); + } + + /** + * @see StepInterface + */ + public function checkRequirements() + { + return array(); + } + + /** + * checkOptionalSettings + */ + public function checkOptionalSettings() + { + return array(); + } + + /** + * @see StepInterface + */ + public function update(StepInterface $data) + { + return array('secret' => $data->secret); + } + + /** + * @see StepInterface + */ + public function getTemplate() + { + return 'SensioDistributionBundle:Configurator/Step:secret.html.twig'; + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/StepInterface.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/StepInterface.php new file mode 100644 index 0000000..94767ee --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Configurator/Step/StepInterface.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\DistributionBundle\Configurator\Step; + +use Symfony\Component\Form\Type\FormTypeInterface; + +/** + * StepInterface. + * + * @author Marc Weistroff + */ +interface StepInterface +{ + /** + * __construct + * + * @param array $parameters + */ + function __construct(array $parameters); + + /** + * Returns the form used for configuration. + * + * @return FormTypeInterface + */ + function getFormType(); + + /** + * Checks for requirements. + * + * @return array + */ + function checkRequirements(); + + /** + * Checks for optional setting it could be nice to have. + * + * @return array + */ + function checkOptionalSettings(); + + /** + * Returns the template to be renderer for this step. + * + * @return string + */ + function getTemplate(); + + /** + * Updates form data parameters. + * + * @param array $parameters + * @return array + */ + function update(StepInterface $data); +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Controller/ConfiguratorController.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Controller/ConfiguratorController.php new file mode 100644 index 0000000..07214cf --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Controller/ConfiguratorController.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\DistributionBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAware; +use Symfony\Component\HttpFoundation\RedirectResponse; + +/** + * ConfiguratorController. + * + * @author Fabien Potencier + */ +class ConfiguratorController extends ContainerAware +{ + /** + * @return Response A Response instance + */ + public function stepAction($index = 0) + { + $configurator = $this->container->get('sensio.distribution.webconfigurator'); + + $step = $configurator->getStep($index); + $form = $this->container->get('form.factory')->create($step->getFormType(), $step); + + $request = $this->container->get('request'); + if ('POST' === $request->getMethod()) { + $form->bindRequest($request); + if ($form->isValid()) { + $configurator->mergeParameters($step->update($form->getData())); + $configurator->write(); + + $index++; + + if ($index < $configurator->getStepCount()) { + return new RedirectResponse($this->container->get('router')->generate('_configurator_step', array('index' => $index))); + } + + return new RedirectResponse($this->container->get('router')->generate('_configurator_final')); + } + } + + return $this->container->get('templating')->renderResponse($step->getTemplate(), array( + 'form' => $form->createView(), + 'index' => $index, + 'count' => $configurator->getStepCount(), + 'version' => $this->getVersion(), + )); + } + + public function checkAction() + { + $configurator = $this->container->get('sensio.distribution.webconfigurator'); + + // Trying to get as much requirements as possible + $majors = $configurator->getRequirements(); + $minors = $configurator->getOptionalSettings(); + + $url = $this->container->get('router')->generate('_configurator_step', array('index' => 0)); + + if (empty($majors) && empty($minors)) { + return new RedirectResponse($url); + } + + return $this->container->get('templating')->renderResponse('SensioDistributionBundle::Configurator/check.html.twig', array( + 'majors' => $majors, + 'minors' => $minors, + 'url' => $url, + 'version' => $this->getVersion(), + )); + } + + public function finalAction() + { + $configurator = $this->container->get('sensio.distribution.webconfigurator'); + $configurator->clean(); + + try { + $welcomeUrl = $this->container->get('router')->generate('_welcome'); + } catch (\Exception $e) { + $welcomeUrl = null; + } + + return $this->container->get('templating')->renderResponse('SensioDistributionBundle::Configurator/final.html.twig', array( + 'welcome_url' => $welcomeUrl, + 'parameters' => $configurator->render(), + 'yml_path' => $this->container->getParameter('kernel.root_dir').'/config/parameters.yml', + 'is_writable' => $configurator->isFileWritable(), + 'version' => $this->getVersion(), + )); + } + + public function getVersion() + { + $kernel = $this->container->get('kernel'); + + return $kernel::VERSION; + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/DependencyInjection/SensioDistributionExtension.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/DependencyInjection/SensioDistributionExtension.php new file mode 100644 index 0000000..4363d57 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/DependencyInjection/SensioDistributionExtension.php @@ -0,0 +1,33 @@ + + */ +class SensioDistributionExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + + $loader->load('webconfigurator.xml'); + } + + public function getNamespace() + { + return 'http://symfony.com/schema/dic/symfony/sensiodistribution'; + } + + public function getAlias() + { + return 'sensio_distribution'; + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/LICENSE b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/LICENSE new file mode 100644 index 0000000..c6aa7e3 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010,2011 Fabien Potencier + +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/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/bin/build.sh b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/bin/build.sh new file mode 100755 index 0000000..ad9ee9b --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/bin/build.sh @@ -0,0 +1,102 @@ +#!/bin/sh + +# This file is part of the Symfony Standard Edition. +# +# (c) Fabien Potencier +# +# For the full copyright and license information, please view the LICENSE +# file that was distributed with this source code. + +DIR=`php -r "echo realpath(dirname(\\$_SERVER['argv'][0]));"` +cd $DIR +VERSION=`grep ' VERSION ' vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php | sed -E "s/.*'(.+)'.*/\1/g"` + +if [ ! -d "$DIR/build" ]; then + mkdir -p $DIR/build +fi + +# Without vendors +rm -rf /tmp/Symfony +mkdir /tmp/Symfony +cp -r app /tmp/Symfony/ +cp -r src /tmp/Symfony/ +cp -r web /tmp/Symfony/ +cp README.md /tmp/Symfony/ +cp LICENSE /tmp/Symfony/ +cp composer.json /tmp/Symfony/ +cp composer.lock /tmp/Symfony/ +cd /tmp/Symfony +sudo rm -rf app/cache/* app/logs/* .git* +chmod 777 app/cache app/logs + +# DS_Store cleanup +find . -name .DS_Store | xargs rm -rf - + +cd .. +# avoid the creation of ._* files +export COPY_EXTENDED_ATTRIBUTES_DISABLE=true +export COPYFILE_DISABLE=true +tar zcpf $DIR/build/Symfony_Standard_$VERSION.tgz Symfony +sudo rm -f $DIR/build/Symfony_Standard_$VERSION.zip +zip -rq $DIR/build/Symfony_Standard_$VERSION.zip Symfony + +# With vendors +cd $DIR +rm -rf /tmp/vendor +mkdir /tmp/vendor +TARGET=/tmp/vendor + +if [ ! -d "$DIR/vendor" ]; then + echo "The master vendor directory does not exist" + exit +fi + +cp -r $DIR/vendor/* $TARGET/ + +# Doctrine ORM +cd $TARGET/doctrine/orm && rm -rf UPGRADE* build* bin tests tools lib/vendor + +# Doctrine DBAL +cd $TARGET/doctrine/dbal && rm -rf bin build* tests lib/vendor + +# Doctrine Common +cd $TARGET/doctrine/common && rm -rf build* tests lib/vendor + +# Swiftmailer +cd $TARGET/swiftmailer/swiftmailer && rm -rf CHANGES README* build* docs notes test-suite tests create_pear_package.php package* + +# Symfony +cd $TARGET/symfony/symfony && rm -rf README.md phpunit.xml* tests *.sh vendor + +# Twig +cd $TARGET/twig/twig && rm -rf AUTHORS CHANGELOG README.markdown bin doc package.xml.tpl phpunit.xml* test + +# Twig Extensions +cd $TARGET/twig/extensions && rm -rf README doc phpunit.xml* test + +# Monolog +cd $TARGET/monolog/monolog && rm -rf README.markdown phpunit.xml* tests + +# Sensio +cd $TARGET/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/ && rm -rf phpunit.xml* Tests CHANGELOG* +cd $TARGET/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/ && rm -rf phpunit.xml* Tests CHANGELOG* +cd $TARGET/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/ && rm -rf phpunit.xml* Tests CHANGELOG* + +# JMS +cd $TARGET/jms/metadata && rm -rf README.rst phpunit.xml* tests +cd $TARGET/jms/cg && rm -rf README.rst phpunit.xml* tests +cd $TARGET/jms/aop-bundle/JMS/AopBundle && rm -rf phpunit.xml* Tests +cd $TARGET/jms/di-extra-bundle/JMS/DiExtraBundle && rm -rf phpunit.xml* Tests +cd $TARGET/jms/security-extra-bundle/JMS/SecurityExtraBundle/ && rm -rf phpunit.xml* Tests + +# cleanup +find $TARGET -name .git | xargs rm -rf - +find $TARGET -name .gitignore | xargs rm -rf - +find $TARGET -name .gitmodules | xargs rm -rf - +find $TARGET -name .svn | xargs rm -rf - + +cd /tmp/ +mv /tmp/vendor /tmp/Symfony/ +tar zcpf $DIR/build/Symfony_Standard_Vendors_$VERSION.tgz Symfony +sudo rm -f $DIR/build/Symfony_Standard_Vendors_$VERSION.zip +zip -rq $DIR/build/Symfony_Standard_Vendors_$VERSION.zip Symfony diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/bin/build_bootstrap.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/bin/build_bootstrap.php new file mode 100755 index 0000000..4ac315b --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/bin/build_bootstrap.php @@ -0,0 +1,26 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +$argv = $_SERVER['argv']; + +// allow the base path to be passed as the first argument, or default +if (isset($argv[1])) { + $appDir = $argv[1]; +} else { + if (!$appDir = realpath(__DIR__.'/../../../../../../../app')) { + exit('Looks like you don\'t have a standard layout.'); + } +} + +require_once $appDir.'/autoload.php'; + +\Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::doBuildBootstrap($appDir); diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/config/routing/webconfigurator.xml b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/config/routing/webconfigurator.xml new file mode 100644 index 0000000..6dae8b5 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/config/routing/webconfigurator.xml @@ -0,0 +1,18 @@ + + + + + + SensioDistributionBundle:Configurator:check + + + + SensioDistributionBundle:Configurator:step + + + + SensioDistributionBundle:Configurator:final + + diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/config/webconfigurator.xml b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/config/webconfigurator.xml new file mode 100644 index 0000000..fcf3e64 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/config/webconfigurator.xml @@ -0,0 +1,17 @@ + + + + + + Sensio\Bundle\DistributionBundle\Configurator\Configurator + + + + + %kernel.root_dir% + + + + diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/css/configure.css b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/css/configure.css new file mode 100644 index 0000000..359bba1 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/css/configure.css @@ -0,0 +1,153 @@ +@import url("install.css"); + +h1 { + margin-top: 10px; + font-size: 26px; +} + +#symfony-wrapper { + padding-top: 0; +} + +#symfony-search { + position: absolute; + top: 50px; + right: 30px; +} + +#symfony-search-field { + width: 190px; +} + +#symfony-search label { + display: block; + float: left; + width: 20px; + height: 25px; + background: url(../images/search.png) no-repeat left 5px; +} + +#symfony-search label span { + display: none; +} + +input[type=text] { + border: 1px solid #DADADA; + background: white url(../images/field-background.gif) repeat-x left top; + padding: 5px 6px; + color: #565656; + font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + font-size: 12px; +} + + +.symfony-button-grey, .symfony-button-green { + font-size: 0.85em; + font-weight: bold; + cursor: pointer; + display: inline-block; + outline: none; + text-align: center; + text-transform: uppercase; + padding: 3px 10px; + text-shadow: 0 1px 1px rgba(0,0,0,.3); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.symfony-button-grey { + color: #868686; + font-weight: normal; + padding: 5px 10px; + border: solid 1px #d7d7d7; + background: #ffffff; + background: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#d7d7d7)); + background: -moz-linear-gradient(top, #ffffff, #d7d7d7); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#d7d7d7'); +} + +.symfony-button-green { + padding: 5px 12px; + color: white; + border: solid 1px #a7da39; + background: #a7da39; + background: -webkit-gradient(linear, left top, left bottom, from(#a7da39), to(#6a9211)); + background: -moz-linear-gradient(top, #a7da39, #6a9211); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#a7da39', endColorstr='#6a9211'); +} + +.symfony-block-steps span { + display: inline-block; + padding: 2px 3px; + font-size: 11px; + line-height: 15px; + color: #868686; + font-weight: bold; + text-transform: uppercase; +} + +.symfony-block-steps span.selected { + background-color: #aacd4e; + color: #FFFFFF; +} + + +.symfony-form-row { + padding-bottom: 40px; +} + +.symfony-form-column { + width: 430px; + float: left; +} + +.symfony-form-footer { + padding-top: 20px; + clear: both; +} + +.symfony-form-field { + height: 20px; +} + +.symfony-form-row label { + display: block; + padding-bottom: 8px; +} + +.symfony-form-field input[type=text], +.symfony-form-field input[type=password], +.symfony-form-field textarea, +.symfony-form-field select { + font-size: 13px; + color: #565656; + width: 200px; +} + +.symfony-form-field input[type=text], +.symfony-form-field input[type=password], +.symfony-form-field textarea { + border: 1px solid #dadada; + background: #FFFFFF url(../images/background-textfield.gif) repeat-x left top; + width: 194px; + padding: 5px 6px; +} + +.symfony-form-errors ul { + padding: 0; +} + +.symfony-form-errors li { + background: url(../images/notification.gif) no-repeat left 6px; + font-size: 11px; + line-height: 16px; + color: #759e1a; + padding: 10px 25px; +} + +.symfony-configuration { + margin: 10px 0; + width: 100%; + height: 240px; +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/css/install.css b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/css/install.css new file mode 100644 index 0000000..7edb396 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/css/install.css @@ -0,0 +1,137 @@ +/* +Copyright (c) 2010, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.com/yui/license.html +version: 2.8.2r1 + +Reset +*/ + +html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;} + +html, body { + background-color: #EFEFEF; +} + +body { + font-size: 14px; + font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + color: #313131; +} + +a { + color: #08C; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +strong { + font-weight: bold; +} + +em { + font-style: italic; +} + +h1, h2, h3 { + font-family: Georgia, "Times New Roman", Times, serif; + color: #404040; +} + +h1 { + font-size: 45px; + padding-bottom: 30px; +} + +h2 { + font-weight: bold; + color: #FFFFFF; + /* Font is reset to sans-serif (like body) */ + font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + margin-bottom: 10px; + background-color: #aacd4e; + padding: 2px 4px; + display: inline-block; + text-transform: uppercase; + +} + +p { + line-height: 20px; + padding-bottom: 20px; +} + +ul a { + background: url(../images/blue-arrow.png) no-repeat right 6px; + padding-right: 10px; +} + +ul, ol { + padding-left: 20px; +} + +li { + padding-bottom: 18px; +} + +ol li { + list-style-type: decimal; +} + +ul li { + list-style-type: none; +} + +#symfony-header { + position: relative; + padding: 30px 30px 20px 30px; +} + +#symfony-wrapper { + width: 970px; + margin: 0 auto; + padding-top: 50px; +} + +#symfony-content { + background-color: white; + border: 1px solid #DFDFDF; + padding: 50px; + -moz-border-radius: 16px; + -webkit-border-radius: 16px; + border-radius: 16px; + margin-bottom: 5px; +} + + +.symfony-blocks-install { + overflow: hidden; +} + +.symfony-blocks-install .symfony-block-logo { + float: left; + width: 358px; +} + +.symfony-blocks-install .symfony-block-content { + float: left; + width: 510px; +} + +.symfony-install-continue { + font-size: 0.95em; + padding-left: 0; +} + +.symfony-install-continue li { + padding-bottom: 10px; +} + +.version { + text-align: right; + font-size: 10px; + margin-right: 20px; +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/background-textfield.gif b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/background-textfield.gif new file mode 100644 index 0000000..7c0efc1 Binary files /dev/null and b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/background-textfield.gif differ diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/blue-arrow.png b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/blue-arrow.png new file mode 100644 index 0000000..fa82d4b Binary files /dev/null and b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/blue-arrow.png differ diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/favicon.ico b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/favicon.ico new file mode 100644 index 0000000..8648036 Binary files /dev/null and b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/favicon.ico differ diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/logo-big.gif b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/logo-big.gif new file mode 100644 index 0000000..721f12a Binary files /dev/null and b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/logo-big.gif differ diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/logo-small.gif b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/logo-small.gif new file mode 100644 index 0000000..703cf45 Binary files /dev/null and b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/logo-small.gif differ diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/notification.gif b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/notification.gif new file mode 100644 index 0000000..017fac8 Binary files /dev/null and b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/public/webconfigurator/images/notification.gif differ diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/Step/doctrine.html.twig b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/Step/doctrine.html.twig new file mode 100644 index 0000000..444d592 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/Step/doctrine.html.twig @@ -0,0 +1,31 @@ +{% extends "SensioDistributionBundle::Configurator/layout.html.twig" %} + +{% block title %}Symfony - Configure database{% endblock %} + +{% block content %} + {% form_theme form "SensioDistributionBundle::Configurator/form.html.twig" %} + {% include "SensioDistributionBundle::Configurator/steps.html.twig" with { "index": index, "count": count } %} + +

    Configure your Database

    +

    If your website needs a database connection, please configure it here.

    + + {{ form_errors(form) }} +
    +
    + {{ form_row(form.driver) }} + {{ form_row(form.host) }} + {{ form_row(form.name) }} +
    +
    + {{ form_row(form.user) }} + {{ form_row(form.password) }} +
    + + {{ form_rest(form) }} + + + +{% endblock %} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/Step/secret.html.twig b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/Step/secret.html.twig new file mode 100644 index 0000000..4718e7d --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/Step/secret.html.twig @@ -0,0 +1,44 @@ +{% extends "SensioDistributionBundle::Configurator/layout.html.twig" %} + +{% block title %}Symfony - Configure global Secret{% endblock %} + +{% block content %} + {% form_theme form "SensioDistributionBundle::Configurator/form.html.twig" %} + {% include "SensioDistributionBundle::Configurator/steps.html.twig" with { "index": index, "count": count } %} + +

    Global Secret

    +

    Configure the global secret for your website (the secret is used for the CSRF protection among other things):

    + + {{ form_errors(form) }} +
    +
    + {{ form_label(form.secret) }} +
    + {{ form_widget(form.secret) }} + Generate +
    + {{ form_errors(form.secret) }} +
    +
    +
    + + {{ form_rest(form) }} + + + + + + +{% endblock %} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/check.html.twig b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/check.html.twig new file mode 100644 index 0000000..52a0648 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/check.html.twig @@ -0,0 +1,43 @@ +{% extends "SensioDistributionBundle::Configurator/layout.html.twig" %} + +{% block content %} + {% if majors|length %} +

    Major Problems that need to be fixed now

    +

    + We have detected {{ majors|length }} major problems. + You must fix them before continuing: +

    +
      + {% for message in majors %} +
    1. {{ message }}
    2. + {% endfor %} +
    + {% endif %} + + {% if minors|length %} +

    Some Recommandations

    + +

    + {% if majors|length %} + Additionally, we + {% else %} + We + {% endif %} + have detected some minor problems that we recommend you to fix to have a better Symfony + experience: + +

      + {% for message in minors %} +
    1. {{ message }}
    2. + {% endfor %} +
    +

    + + {% endif %} + + {% if not majors|length %} + + {% endif %} +{% endblock %} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/final.html.twig b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/final.html.twig new file mode 100644 index 0000000..f4a0237 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/final.html.twig @@ -0,0 +1,28 @@ +{% extends "SensioDistributionBundle::Configurator/layout.html.twig" %} + +{% block content_class %}config_done{% endblock %} +{% block content %} +

    Well done!

    + {% if is_writable %} +

    Your distribution is configured!

    + {% else %} +

    Your distribution is almost configured but...

    + {% endif %} +

    + + {% if is_writable %} + Your parameters.yml file has been overwritten with these parameters (in {{ yml_path }}): + {% else %} + Your parameters.yml file is not writeable! Here are the parameters you can copy and paste in {{ yml_path }}: + {% endif %} + +

    + + + + {% if welcome_url %} + + {% endif %} +{% endblock %} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/form.html.twig b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/form.html.twig new file mode 100644 index 0000000..ad7f8e6 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/form.html.twig @@ -0,0 +1,31 @@ +{% extends "form_div_layout.html.twig" %} + +{% block field_rows %} +
    + {{ form_errors(form) }} +
    + {% for child in form.children %} + {{ form_row(child) }} + {% endfor %} +{% endblock field_rows %} + +{% block field_row %} +
    + {{ form_label(form) }} +
    + {{ form_widget(form) }} +
    + {{ form_errors(form) }} +
    +
    +
    +{% endblock %} + +{% block field_label %} + +{% endblock %} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/layout.html.twig b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/layout.html.twig new file mode 100644 index 0000000..3238fc3 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/layout.html.twig @@ -0,0 +1,20 @@ + + + + + + {% block title %}Web Configurator Bundle{% endblock %} + + + +
    +
    + Symfony logo +
    +
    + {% block content %}{% endblock %} +
    +
    Symfony Standard Edition v.{{ version }}
    +
    + + diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/steps.html.twig b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/steps.html.twig new file mode 100644 index 0000000..c228827 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/Resources/views/Configurator/steps.html.twig @@ -0,0 +1,14 @@ +
    + {% for i in 1..count %} + + {% if i == index + 1 %} + Step {{ i }} + {% else %} + Step {{ i }} + {% endif %} + + {% if i != count %} + > + {% endif %} + {% endfor %} +
    diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/SensioDistributionBundle.php b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/SensioDistributionBundle.php new file mode 100644 index 0000000..88a2913 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/SensioDistributionBundle.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\DistributionBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Sensio\Bundle\DistributionBundle\Configurator\Step\DoctrineStep; +use Sensio\Bundle\DistributionBundle\Configurator\Step\SecretStep; + +/** + * SensioDistributionBundle. + * + * @author Fabien Potencier + * @author Marc Weistroff + */ +class SensioDistributionBundle extends Bundle +{ + public function boot() + { + $configurator = $this->container->get('sensio.distribution.webconfigurator'); + $configurator->addStep(new DoctrineStep($configurator->getParameters())); + $configurator->addStep(new SecretStep($configurator->getParameters())); + } +} diff --git a/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/composer.json b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/composer.json new file mode 100644 index 0000000..1a60e63 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/composer.json @@ -0,0 +1,20 @@ +{ + "name": "sensio/distribution-bundle", + "description": "The base bundle for the Symfony Distributions", + "keywords": ["distribution","configuration"], + "type": "symfony-bundle", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "symfony/framework-bundle": "2.1.*" + }, + "autoload": { + "psr-0": { "Sensio\\Bundle\\DistributionBundle": "" } + }, + "target-dir": "Sensio/Bundle/DistributionBundle" +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/.travis.yml b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/.travis.yml new file mode 100644 index 0000000..96f6fb3 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/.travis.yml @@ -0,0 +1,9 @@ +language: php + +php: + - 5.3 + - 5.4 + +before_script: + - wget http://getcomposer.org/composer.phar + - php composer.phar install diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Cache.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Cache.php new file mode 100644 index 0000000..959174c --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Cache.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The Cache class handles the @Cache annotation parts. + * + * @author Fabien Potencier + * @Annotation + */ +class Cache extends ConfigurationAnnotation +{ + /** + * The expiration date as a valid date for the strtotime() function. + * + * @var string + */ + protected $expires; + + /** + * The number of seconds that the response is considered fresh by a private + * cache like a web browser. + * + * @var integer + */ + protected $maxage; + + /** + * The number of seconds that the response is considered fresh by a public + * cache like a reverse proxy cache. + * + * @var integer + */ + protected $smaxage; + + /** + * Whether or not the response is public or not. + * + * @var integer + */ + protected $public; + + /** + * Additional "Vary:"-headers + * + * @var array + */ + protected $vary = array(); + + /** + * Returns the expiration date for the Expires header field. + * + * @return string + */ + public function getExpires() + { + return $this->expires; + } + + /** + * Sets the expiration date for the Expires header field. + * + * @param string $expires A valid php date + */ + public function setExpires($expires) + { + $this->expires = $expires; + } + + /** + * Sets the number of seconds for the max-age cache-control header field. + * + * @param integer $maxage A number of seconds + */ + public function setMaxAge($maxage) + { + $this->maxage = $maxage; + } + + /** + * Returns the number of seconds the response is considered fresh by a + * private cache. + * + * @return integer + */ + public function getMaxAge() + { + return $this->maxage; + } + + /** + * Sets the number of seconds for the s-maxage cache-control header field. + * + * @param integer $smaxage A number of seconds + */ + public function setSMaxAge($smaxage) + { + $this->smaxage = $smaxage; + } + + /** + * Returns the number of seconds the response is considered fresh by a + * public cache. + * + * @return integer + */ + public function getSMaxAge() + { + return $this->smaxage; + } + + /** + * Returns whether or not a response is public. + * + * @return Boolean + */ + public function isPublic() + { + return (Boolean) $this->public; + } + + /** + * Sets a response public. + * + * @param Boolean $public A boolean value + */ + public function setPublic($public) + { + $this->public = (Boolean) $public; + } + + /** + * Returns the custom "Vary"-headers + * + * @return array + */ + public function getVary() + { + return $this->vary; + } + + /** + * Add additional "Vary:"-headers + * + * @param array $vary + */ + public function setVary($vary) + { + $this->vary = $vary; + } + + /** + * Returns the annotation alias name. + * + * @return string + * @see ConfigurationInterface + */ + public function getAliasName() + { + return 'cache'; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ConfigurationAnnotation.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ConfigurationAnnotation.php new file mode 100644 index 0000000..678de1a --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ConfigurationAnnotation.php @@ -0,0 +1,22 @@ + + */ +abstract class ConfigurationAnnotation implements ConfigurationInterface +{ + public function __construct(array $values) + { + foreach ($values as $k => $v) { + if (!method_exists($this, $name = 'set'.$k)) { + throw new \RuntimeException(sprintf('Unknown key "%s" for annotation "@%s".', $k, get_class($this))); + } + + $this->$name($v); + } + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ConfigurationInterface.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ConfigurationInterface.php new file mode 100644 index 0000000..a30df65 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ConfigurationInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * ConfigurationInterface. + * + * @author Fabien Potencier + */ +interface ConfigurationInterface +{ + /** + * Returns the alias name for an annotated configuration. + * + * @return string + */ + function getAliasName(); +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Method.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Method.php new file mode 100644 index 0000000..2edad3b --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Method.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The Method class handles the @Method annotation parts. + * + * @author Fabien Potencier + * @Annotation + */ +class Method extends ConfigurationAnnotation +{ + /** + * An array of restricted HTTP methods. + * + * @var array + */ + protected $methods = array(); + + /** + * Returns the array of HTTP methods. + * + * @return array + */ + public function getMethods() + { + return $this->methods; + } + + /** + * Sets the HTTP methods. + * + * @param array|string $methods An HTTP method or an array of HTTP methods + */ + public function setMethods($methods) + { + $this->methods = is_array($methods) ? $methods : array($methods); + } + + /** + * Sets the HTTP methods. + * + * @param array|string $methods An HTTP method or an array of HTTP methods + */ + public function setValue($methods) + { + $this->setMethods($methods); + } + + /** + * Returns the annotation alias name. + * + * @return string + * @see ConfigurationInterface + */ + public function getAliasName() + { + return 'method'; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ParamConverter.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ParamConverter.php new file mode 100644 index 0000000..89afc2e --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/ParamConverter.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The ParamConverter class handles the @ParamConverter annotation parts. + * + * @ParamConverter("post", class="BlogBundle:Post") + * + * @author Fabien Potencier + * @Annotation + */ +class ParamConverter extends ConfigurationAnnotation +{ + /** + * The parameter name. + * + * @var string + */ + protected $name; + + /** + * The parameter class. + * + * @var string + */ + protected $class; + + /** + * An array of options. + * + * @var array + */ + protected $options = array(); + + /** + * Whether or not the parameter is optional. + * + * @var Boolean + */ + protected $optional = false; + + /** + * Returns the parameter name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the parameter name. + * + * @param string $name The parameter name + */ + public function setValue($name) + { + $this->setName($name); + } + + /** + * Sets the parameter name. + * + * @param string $name The parameter name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Returns the parameter class name. + * + * @return string $name + */ + public function getClass() + { + return $this->class; + } + + /** + * Sets the parameter class name. + * + * @param string $class The parameter class name + */ + public function setClass($class) + { + $this->class = $class; + } + + /** + * Returns an array of options. + * + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * Sets an array of options. + * + * @param array $options An array of options + */ + public function setOptions($options) + { + $this->options = $options; + } + + /** + * Sets whether or not the parameter is optional. + * + * @param Boolean $optional Wether the parameter is optional + */ + public function setIsOptional($optional) + { + $this->optional = (Boolean) $optional; + } + + /** + * Returns whether or not the parameter is optional. + * + * @return Boolean + */ + public function isOptional() + { + return $this->optional; + } + + /** + * Returns the annotation alias name. + * + * @return string + * @see ConfigurationInterface + */ + public function getAliasName() + { + return 'converters'; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Route.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Route.php new file mode 100644 index 0000000..efdaf76 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Route.php @@ -0,0 +1,33 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * @author Kris Wallsmith + * @Annotation + */ +class Route extends BaseRoute +{ + protected $service; + + public function setService($service) + { + $this->service = $service; + } + + public function getService() + { + return $this->service; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Template.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Template.php new file mode 100644 index 0000000..bd87e17 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Configuration/Template.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * The Template class handles the @Template annotation parts. + * + * @author Fabien Potencier + * @Annotation + */ +class Template extends ConfigurationAnnotation +{ + /** + * The template reference. + * + * @var TemplateReference + */ + protected $template; + + /** + * The template engine used when a specific template isnt specified + * + * @var string + */ + protected $engine = 'twig'; + + /** + * The associative array of template variables. + * + * @var array + */ + protected $vars = array(); + + /** + * Should the template be streamed? + * + * @var Boolean + */ + protected $streamable = false; + + /** + * Returns the array of templates variables. + * + * @return array + */ + public function getVars() + { + return $this->vars; + } + + /** + * @param Boolean $streamable + */ + public function setIsStreamable($streamable) + { + $this->streamable = $streamable; + } + + /** + * @return Boolean + */ + public function isStreamable() + { + return (Boolean) $this->streamable; + } + + /** + * Sets the template variables + * + * @param array $vars The template variables + */ + public function setVars($vars) + { + $this->vars = $vars; + } + + /** + * Returns the engine used when guessing template names + * + * @return string + */ + public function getEngine() + { + return $this->engine; + } + + /** + * Sets the engine used when guessing template names + * + * @param string + */ + public function setEngine($engine) + { + $this->engine = $engine; + } + + /** + * Sets the template logic name. + * + * @param string $template The template logic name + */ + public function setValue($template) + { + $this->setTemplate($template); + } + + /** + * Returns the template reference. + * + * @return TemplateReference + */ + public function getTemplate() + { + return $this->template; + } + + /** + * Sets the template reference. + * + * @param TemplateReference|string $template The template reference + */ + public function setTemplate($template) + { + $this->template = $template; + } + + /** + * Returns the annotation alias name. + * + * @return string + * @see ConfigurationInterface + */ + public function getAliasName() + { + return 'template'; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/Compiler/AddParamConverterPass.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/Compiler/AddParamConverterPass.php new file mode 100644 index 0000000..b739e0b --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/Compiler/AddParamConverterPass.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Adds tagged request.param_converter services to converter.manager service + * + * @author Fabien Potencier + */ +class AddParamConverterPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (false === $container->hasDefinition('sensio_framework_extra.converter.manager')) { + return; + } + + $definition = $container->getDefinition('sensio_framework_extra.converter.manager'); + foreach ($container->findTaggedServiceIds('request.param_converter') as $id => $attributes) { + $definition->addMethodCall('add', array(new Reference($id), isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0)); + } + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/Configuration.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..4c1b2d1 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/Configuration.php @@ -0,0 +1,55 @@ + + */ +class Configuration +{ + /** + * Generates the configuration tree. + * + * @return Symfony\Component\Config\Definition\NodeInterface + */ + public function getConfigTree() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('sensio_framework_extra', 'array'); + + $rootNode + ->children() + ->arrayNode('router') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('annotations')->defaultTrue()->end() + ->end() + ->end() + ->arrayNode('request') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('converters')->defaultTrue()->end() + ->end() + ->end() + ->arrayNode('view') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('annotations')->defaultTrue()->end() + ->end() + ->end() + ->arrayNode('cache') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('annotations')->defaultTrue()->end() + ->end() + ->end() + ->end() + ; + + return $treeBuilder->buildTree(); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/SensioFrameworkExtraExtension.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/SensioFrameworkExtraExtension.php new file mode 100644 index 0000000..7bee12d --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/DependencyInjection/SensioFrameworkExtraExtension.php @@ -0,0 +1,81 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * SensioFrameworkExtraExtension. + * + * @author Fabien Potencier + */ +class SensioFrameworkExtraExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + + $processor = new Processor(); + $configuration = new Configuration(); + + $config = $processor->process($configuration->getConfigTree(), $configs); + + $loader->load('services.xml'); + + $annotationsToLoad = array(); + + if ($config['router']['annotations']) { + $annotationsToLoad[] = 'routing.xml'; + } + + if ($config['request']['converters']) { + $annotationsToLoad[] = 'converters.xml'; + } + + if ($config['view']['annotations']) { + $annotationsToLoad[] = 'view.xml'; + } + + if ($config['cache']['annotations']) { + $annotationsToLoad[] = 'cache.xml'; + } + + if ($annotationsToLoad) { + // must be first + $loader->load('annotations.xml'); + + foreach ($annotationsToLoad as $config) { + $loader->load($config); + } + } + } + + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + */ + public function getXsdValidationBasePath() + { + return __DIR__.'/../Resources/config/schema'; + } + + public function getNamespace() + { + return 'http://symfony.com/schema/dic/symfony_extra'; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/CacheListener.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/CacheListener.php new file mode 100644 index 0000000..3dd6e87 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/CacheListener.php @@ -0,0 +1,65 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * The CacheListener class has the responsability to modify the + * Response object when a controller uses the @Cache annotation. + * + * @author Fabien Potencier + */ +class CacheListener +{ + /** + * Modifies the response to apply HTTP expiration header fields. + * + * @param FilterResponseEvent $event The notified event + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$configuration = $event->getRequest()->attributes->get('_cache')) { + return; + } + + $response = $event->getResponse(); + + if (!$response->isSuccessful()) { + return; + } + + if (null !== $configuration->getSMaxAge()) { + $response->setSharedMaxAge($configuration->getSMaxAge()); + } + + if (null !== $configuration->getMaxAge()) { + $response->setMaxAge($configuration->getMaxAge()); + } + + if (null !== $configuration->getExpires()) { + $date = \DateTime::createFromFormat('U', strtotime($configuration->getExpires()), new \DateTimeZone('UTC')); + $response->setExpires($date); + } + + if (null !== $configuration->getVary()) { + $response->setVary($configuration->getVary()); + } + + if ($configuration->isPublic()) { + $response->setPublic(); + } + + $event->setResponse($response); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/ControllerListener.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/ControllerListener.php new file mode 100644 index 0000000..33f5d62 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/ControllerListener.php @@ -0,0 +1,64 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * The ControllerListener class parses annotation blocks located in + * controller classes. + * + * @author Fabien Potencier + */ +class ControllerListener +{ + /** + * @var \Doctrine\Common\Annotations\Reader + */ + protected $reader; + + /** + * Constructor. + * + * @param Reader $reader An Reader instance + */ + public function __construct(Reader $reader) + { + $this->reader = $reader; + } + + /** + * Modifies the Request object to apply configuration information found in + * controllers annotations like the template to render or HTTP caching + * configuration. + * + * @param FilterControllerEvent $event A FilterControllerEvent instance + */ + public function onKernelController(FilterControllerEvent $event) + { + if (!is_array($controller = $event->getController())) { + return; + } + + $object = new \ReflectionObject($controller[0]); + $method = $object->getMethod($controller[1]); + + $request = $event->getRequest(); + foreach (array_merge($this->reader->getClassAnnotations($object), $this->reader->getMethodAnnotations($method)) as $configuration) { + if ($configuration instanceof ConfigurationInterface) { + $request->attributes->set('_'.$configuration->getAliasName(), $configuration); + } + } + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/ParamConverterListener.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/ParamConverterListener.php new file mode 100644 index 0000000..b6df11e --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/ParamConverterListener.php @@ -0,0 +1,85 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * The ParamConverterListener handles the @ParamConverter annotation. + * + * @author Fabien Potencier + */ +class ParamConverterListener +{ + /** + * @var Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager + */ + protected $manager; + + /** + * Constructor. + * + * @param ParamConverterManager $manager A ParamConverterManager instance + */ + public function __construct(ParamConverterManager $manager) + { + $this->manager = $manager; + } + + /** + * Modifies the ParamConverterManager instance. + * + * @param FilterControllerEvent $event A FilterControllerEvent instance + */ + public function onKernelController(FilterControllerEvent $event) + { + $controller = $event->getController(); + $request = $event->getRequest(); + $configurations = array(); + + if ($configuration = $request->attributes->get('_converters')) { + foreach (is_array($configuration) ? $configuration : array($configuration) as $configuration) { + $configurations[$configuration->getName()] = $configuration; + } + } + + if (is_array($controller)) { + $r = new \ReflectionMethod($controller[0], $controller[1]); + } else { + $r = new \ReflectionFunction($controller); + } + + // automatically apply conversion for non-configured objects + foreach ($r->getParameters() as $param) { + if (!$param->getClass()) { + continue; + } + + $name = $param->getName(); + + if (!isset($configurations[$name])) { + $configuration = new ParamConverter(array()); + $configuration->setName($name); + $configuration->setClass($param->getClass()->getName()); + + $configurations[$name] = $configuration; + } + + $configurations[$name]->setIsOptional($param->isOptional()); + } + + $this->manager->apply($request, $configurations); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/TemplateListener.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/TemplateListener.php new file mode 100644 index 0000000..c8e2aa1 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/EventListener/TemplateListener.php @@ -0,0 +1,126 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * The TemplateListener class handles the @Template annotation. + * + * @author Fabien Potencier + */ +class TemplateListener +{ + /** + * @var Symfony\Component\DependencyInjection\ContainerInterface + */ + protected $container; + + /** + * Constructor. + * + * @param ContainerInterface $container The service container instance + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Guesses the template name to render and its variables and adds them to + * the request object. + * + * @param FilterControllerEvent $event A FilterControllerEvent instance + */ + public function onKernelController(FilterControllerEvent $event) + { + if (!is_array($controller = $event->getController())) { + return; + } + + $request = $event->getRequest(); + + if (!$configuration = $request->attributes->get('_template')) { + return; + } + + if (!$configuration->getTemplate()) { + $guesser = $this->container->get('sensio_framework_extra.view.guesser'); + $configuration->setTemplate($guesser->guessTemplateName($controller, $request, $configuration->getEngine())); + } + + $request->attributes->set('_template', $configuration->getTemplate()); + $request->attributes->set('_template_vars', $configuration->getVars()); + $request->attributes->set('_template_streamable', $configuration->isStreamable()); + + // all controller method arguments + if (!$configuration->getVars()) { + $r = new \ReflectionObject($controller[0]); + + $vars = array(); + foreach ($r->getMethod($controller[1])->getParameters() as $param) { + $vars[] = $param->getName(); + } + + $request->attributes->set('_template_default_vars', $vars); + } + } + + /** + * Renders the template and initializes a new response object with the + * rendered template content. + * + * @param GetResponseForControllerResultEvent $event A GetResponseForControllerResultEvent instance + */ + public function onKernelView(GetResponseForControllerResultEvent $event) + { + $request = $event->getRequest(); + $parameters = $event->getControllerResult(); + $templating = $this->container->get('templating'); + + if (null === $parameters) { + if (!$vars = $request->attributes->get('_template_vars')) { + if (!$vars = $request->attributes->get('_template_default_vars')) { + return; + } + } + + $parameters = array(); + foreach ($vars as $var) { + $parameters[$var] = $request->attributes->get($var); + } + } + + if (!is_array($parameters)) { + return $parameters; + } + + if (!$template = $request->attributes->get('_template')) { + return $parameters; + } + + if (!$request->attributes->get('_template_streamable')) { + $event->setResponse($templating->renderResponse($template, $parameters)); + } else { + $callback = function () use ($templating, $template, $parameters) { + return $templating->stream($template, $parameters); + }; + + + $event->setResponse(new StreamedResponse($callback)); + } + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/LICENSE b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/LICENSE new file mode 100644 index 0000000..c6aa7e3 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010,2011 Fabien Potencier + +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/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/README.md b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/README.md new file mode 100644 index 0000000..00918ae --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/README.md @@ -0,0 +1,6 @@ +SensioFrameworkExtraBundle +========================== + +This bundle provides a way to configure your controllers with annotations. + +Read about it on its [official homepage](http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html). diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/DoctrineParamConverter.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/DoctrineParamConverter.php new file mode 100644 index 0000000..321f223 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/DoctrineParamConverter.php @@ -0,0 +1,108 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * DoctrineParamConverter. + * + * @author Fabien Potencier + */ +class DoctrineParamConverter implements ParamConverterInterface +{ + /** + * @var ManagerRegistry + */ + protected $registry; + + public function __construct(ManagerRegistry $registry = null) + { + $this->registry = $registry; + } + + public function apply(Request $request, ConfigurationInterface $configuration) + { + $class = $configuration->getClass(); + $options = $this->getOptions($configuration); + + // find by identifier? + if (false === $object = $this->find($class, $request, $options)) { + // find by criteria + if (false === $object = $this->findOneBy($class, $request, $options)) { + throw new \LogicException('Unable to guess how to get a Doctrine instance from the request information.'); + } + } + + if (null === $object && false === $configuration->isOptional()) { + throw new NotFoundHttpException(sprintf('%s object not found.', $class)); + } + + $request->attributes->set($configuration->getName(), $object); + + return true; + } + + protected function find($class, Request $request, $options) + { + if (!$request->attributes->has('id')) { + return false; + } + + return $this->registry->getRepository($class, $options['entity_manager'])->find($request->attributes->get('id')); + } + + protected function findOneBy($class, Request $request, $options) + { + $criteria = array(); + $metadata = $this->registry->getManager($options['entity_manager'])->getClassMetadata($class); + foreach ($request->attributes->all() as $key => $value) { + if ($metadata->hasField($key)) { + $criteria[$key] = $value; + } + } + + if (!$criteria) { + return false; + } + + return $this->registry->getRepository($class, $options['entity_manager'])->findOneBy($criteria); + } + + public function supports(ConfigurationInterface $configuration) + { + if (null === $this->registry) { + return false; + } + + if (null === $configuration->getClass()) { + return false; + } + + $options = $this->getOptions($configuration); + + // Doctrine Entity? + return ! $this->registry->getManager($options['entity_manager']) + ->getMetadataFactory() + ->isTransient($configuration->getClass()); + } + + protected function getOptions(ConfigurationInterface $configuration) + { + return array_replace(array( + 'entity_manager' => null, + ), $configuration->getOptions()); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/ParamConverterInterface.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/ParamConverterInterface.php new file mode 100644 index 0000000..d5e0390 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/ParamConverterInterface.php @@ -0,0 +1,27 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * + * + * @author Fabien Potencier + */ +interface ParamConverterInterface +{ + function apply(Request $request, ConfigurationInterface $configuration); + + function supports(ConfigurationInterface $configuration); +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/ParamConverterManager.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/ParamConverterManager.php new file mode 100644 index 0000000..85e7c22 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Request/ParamConverter/ParamConverterManager.php @@ -0,0 +1,92 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * Managers converters. + * + * @author Fabien Potencier + * @author Henrik Bjornskov + */ +class ParamConverterManager +{ + /** + * @var array + */ + protected $converters = array(); + + /** + * Applies all converters to the passed configurations and stops when a + * converter is applied it will move on to the next configuration and so on. + * + * @param Request $request + * @param array|object $configurations + */ + public function apply(Request $request, $configurations) + { + if (is_object($configurations)) { + $configurations = array($configurations); + } + + foreach ($configurations as $configuration) { + // If the value is already an instance of the class we are trying to convert it into + // we should continue as no convertion is required + $value = $request->attributes->get($configuration->getName()); + $className = $configuration->getClass(); + if (is_object($value) && $value instanceof $className) { + continue; + } + + foreach ($this->all() as $converter) { + if ($converter->supports($configuration)) { + if ($converter->apply($request, $configuration)) { + continue 2; + } + } + } + } + } + + /** + * Adds a parameter converter. + * + * @param ParamConverterInterface $converter A ParamConverterInterface instance + * @param integer $priority The priority (between -10 and 10) + */ + public function add(ParamConverterInterface $converter, $priority = 0) + { + if (!isset($this->converters[$priority])) { + $this->converters[$priority] = array(); + } + + $this->converters[$priority][] = $converter; + } + + /** + * Returns all registered param converters. + * + * @return array An array of param converters + */ + public function all() + { + krsort($this->converters); + + $converters = array(); + foreach ($this->converters as $all) { + $converters = array_merge($converters, $all); + } + + return $converters; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/annotations.xml b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/annotations.xml new file mode 100644 index 0000000..7d2a7eb --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/annotations.xml @@ -0,0 +1,17 @@ + + + + + + Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/cache.xml b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/cache.xml new file mode 100644 index 0000000..4ebc930 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/cache.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/converters.xml b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/converters.xml new file mode 100644 index 0000000..8f7170e --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/converters.xml @@ -0,0 +1,26 @@ + + + + + + Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener + Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager + Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DoctrineParamConverter + + + + + + + + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/routing.xml b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/routing.xml new file mode 100644 index 0000000..b3b58cd --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/routing.xml @@ -0,0 +1,31 @@ + + + + + + Symfony\Component\Routing\Loader\AnnotationDirectoryLoader + Symfony\Component\Routing\Loader\AnnotationFileLoader + Sensio\Bundle\FrameworkExtraBundle\Routing\AnnotatedRouteControllerLoader + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/services.xml b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/services.xml new file mode 100644 index 0000000..6c76e61 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/services.xml @@ -0,0 +1,17 @@ + + + + + + Sensio\Bundle\FrameworkExtraBundle\Templating\TemplateGuesser + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/view.xml b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/view.xml new file mode 100644 index 0000000..e182902 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/config/view.xml @@ -0,0 +1,18 @@ + + + + + + Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener + + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/annotations/cache.rst b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/annotations/cache.rst new file mode 100644 index 0000000..a05d9f6 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/annotations/cache.rst @@ -0,0 +1,60 @@ +@Cache +====== + +Usage +----- + +The ``@Cache`` annotation makes it easy to define HTTP caching:: + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; + + /** + * @Cache(expires="tomorrow") + */ + public function indexAction() + { + } + +You can also use the annotation on a class to define caching for all methods:: + + /** + * @Cache(expires="tomorrow") + */ + class BlogController extends Controller + { + } + +When there is a conflict between the class configuration and the method +configuration, the latter overrides the former:: + + /** + * @Cache(expires="tomorrow") + */ + class BlogController extends Controller + { + /** + * @Cache(expires="+2 days") + */ + public function indexAction() + { + } + } + +Attributes +---------- + +Here is a list of accepted attributes and their HTTP header equivalent: + +============================== =============== +Annotation Response Method +============================== =============== +``@Cache(expires="tomorrow")`` ``$response->setExpires()`` +``@Cache(smaxage="15")`` ``$response->setSharedMaxAge()`` +``@Cache(maxage="15")`` ``$response->setMaxAge()`` +``@Cache(vary=["Cookie"])`` ``$response->setVary()`` +============================== =============== + +.. note:: + + The ``expires`` attribute takes any valid date understood by the PHP + ``strtotime()`` function. diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/annotations/converters.rst b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/annotations/converters.rst new file mode 100644 index 0000000..1245afb --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/annotations/converters.rst @@ -0,0 +1,101 @@ +@ParamConverter +=============== + +Usage +----- + +The ``@ParamConverter`` annotation calls *converters* to convert request +parameters to objects. These objects are stored as request attributes and so +they can be injected as controller method arguments:: + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; + + /** + * @Route("/blog/{id}") + * @ParamConverter("post", class="SensioBlogBundle:Post") + */ + public function showAction(Post $post) + { + } + +Several things happens under the hood: + +* The converter tries to get a ``SensioBlogBundle:Post`` object from the + request attributes (request attributes comes from route placeholders -- here + ``id``); + +* If no ``Post`` object is found, a ``404`` Response is generated; + +* If a ``Post`` object is found, a new ``post`` request attribute is defined + (accessible via ``$request->attributes->get('post')``); + +* As for any other request attribute, it is automatically injected in the + controller when present in the method signature. + +If you use type hinting as in the example above, you can even omit the +``@ParamConverter`` annotation altogether:: + + // automatic with method signature + public function showAction(Post $post) + { + } + +Built-in Converters +------------------- + +The bundle has only one built-in converter, the Doctrine one. + +Doctrine Converter +~~~~~~~~~~~~~~~~~~ + +By default, the Doctrine converter uses the *default* entity manager. This can +be configured with the ``entity_manager`` option:: + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; + + /** + * @Route("/blog/{id}") + * @ParamConverter("post", class="SensioBlogBundle:Post", options={"entity_manager" = "foo"}) + */ + public function showAction(Post $post) + { + } + +Creating a Converter +-------------------- + +All converters must implement the +:class:`Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\ParamConverterInterface`:: + + namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter; + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface; + use Symfony\Component\HttpFoundation\Request; + + interface ParamConverterInterface + { + function apply(Request $request, ConfigurationInterface $configuration); + + function supports(ConfigurationInterface $configuration); + } + +The ``supports()`` method must return ``true`` when it is able to convert the +given configuration (a ``ParamConverter`` instance). + +The ``ParamConverter`` instance has three information about the annotation: + +* ``name``: The attribute name; +* ``class``: The attribute class name (can be any string representing a class + name); +* ``options``: An array of options + +The ``apply()`` method is called whenever a configuration is supported. Based +on the request attributes, it should set an attribute named +``$configuration->getName()``, which stores an object of class +``$configuration->getClass()``. + +.. tip:: + + Use the ``DoctrineParamConverter`` class as a template for your own converters. diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/annotations/routing.rst b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/annotations/routing.rst new file mode 100644 index 0000000..f993691 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/annotations/routing.rst @@ -0,0 +1,166 @@ +@Route and @Method +================== + +Usage +----- + +The @Route annotation maps a route pattern with a controller:: + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + + class PostController extends Controller + { + /** + * @Route("/") + */ + public function indexAction() + { + // ... + } + } + +The ``index`` action of the ``Post`` controller is now mapped to the ``/`` +URL. This is equivalent to the following YAML configuration: + +.. code-block:: yaml + + blog_home: + pattern: / + defaults: { _controller: SensioBlogBundle:Post:index } + +Like any route pattern, you can define placeholders, requirements, and default +values:: + + /** + * @Route("/{id}", requirements={"id" = "\d+"}, defaults={"foo" = "bar"}) + */ + public function showAction($id) + { + } + +You can also match more than one URL by defining additional ``@Route`` +annotations:: + + /** + * @Route("/", defaults={"id" = 1}) + * @Route("/{id}") + */ + public function showAction($id) + { + } + +Activation +---------- + +The routes need to be imported to be active as any other routing resources +(note the ``annotation`` type): + +.. code-block:: yaml + + # app/config/routing.yml + + # import routes from a controller class + post: + resource: "@SensioBlogBundle/Controller/PostController.php" + type: annotation + +You can also import a whole directory: + +.. code-block:: yaml + + # import routes from a controller directory + blog: + resource: "@SensioBlogBundle/Controller" + type: annotation + +As for any other resource, you can "mount" the routes under a given prefix: + +.. code-block:: yaml + + post: + resource: "@SensioBlogBundle/Controller/PostController.php" + prefix: /blog + type: annotation + +Route Name +---------- + +A route defined with the ``@Route`` annotation is given a default name composed +of the bundle name, the controller name and the action name. That would be +``sensio_blog_post_index`` for the above example; + +The ``name`` attribute can be used to override this default route name:: + + /** + * @Route("/", name="blog_home") + */ + public function indexAction() + { + // ... + } + +Route Prefix +------------ + +A ``@Route`` annotation on a controller class defines a prefix for all action +routes:: + + /** + * @Route("/blog") + */ + class PostController extends Controller + { + /** + * @Route("/{id}") + */ + public function showAction($id) + { + } + } + +The ``show`` action is now mapped to the ``/blog/{id}`` pattern. + +Route Method +------------ + +There is a shortcut ``@Method`` annotation to specify the HTTP method allowed +for the route. To use it, import the ``Method`` annotation namespace:: + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; + + /** + * @Route("/blog") + */ + class PostController extends Controller + { + /** + * @Route("/edit/{id}") + * @Method({"GET", "POST"}) + */ + public function editAction($id) + { + } + } + +The ``edit`` action is now mapped to the ``/blog/edit/{id}`` pattern if the HTTP +method used is either GET or POST. + +The ``@Method`` annotation is only considered when an action is annotated with +``@Route``. + +Controller as Service +--------------------- + +The ``@Route`` annotation on a controller class can also be used to assign the +controller class to a service so that the controller resolver will instantiate +the controller by fetching it from the DI container instead of calling ``new +PostController()`` itself:: + + /** + * @Route(service="my_post_controller_service") + */ + class PostController extends Controller + { + // ... + } diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/annotations/view.rst b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/annotations/view.rst new file mode 100644 index 0000000..d96f94a --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/annotations/view.rst @@ -0,0 +1,74 @@ +@Template +========= + +Usage +----- + +The ``@Template`` annotation associates a controller with a template name:: + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; + + /** + * @Template("SensioBlogBundle:Post:show.html.twig") + */ + public function showAction($id) + { + // get the Post + $post = ...; + + return array('post' => $post); + } + +When using the ``@Template`` annotation, the controller should return an +array of parameters to pass to the view instead of a ``Response`` object. + +.. tip:: + If the action returns a ``Response`` object, the ``@Template`` + annotation is simply ignored. + +If the template is named after the controller and action names, which is the +case for the above example, you can even omit the annotation value:: + + /** + * @Template + */ + public function showAction($id) + { + // get the Post + $post = ...; + + return array('post' => $post); + } + +And if the only parameters to pass to the template are method arguments, you +can use the ``vars`` attribute instead of returning an array. This is very +useful in combination with the ``@ParamConverter`` :doc:`annotation +`:: + + /** + * @ParamConverter("post", class="SensioBlogBundle:Post") + * @Template("SensioBlogBundle:Post:show.html.twig", vars={"post"}) + */ + public function showAction(Post $post) + { + } + +which, thanks to conventions, is equivalent to the following configuration:: + + /** + * @Template(vars={"post"}) + */ + public function showAction(Post $post) + { + } + +You can make it even more concise as all method arguments are automatically +passed to the template if the method returns ``null`` and no ``vars`` +attribute is defined:: + + /** + * @Template + */ + public function showAction(Post $post) + { + } diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/index.rst b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/index.rst new file mode 100644 index 0000000..fcf7a30 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Resources/doc/index.rst @@ -0,0 +1,145 @@ +SensioFrameworkExtraBundle +========================== + +The default Symfony2 ``FrameworkBundle`` implements a basic but robust and +flexible MVC framework. `SensioFrameworkExtraBundle`_ extends it to add sweet +conventions and annotations. It allows for more concise controllers. + +Installation +------------ + +`Download`_ the bundle and put it under the ``Sensio\Bundle\`` namespace. +Then, like for any other bundle, include it in your Kernel class:: + + public function registerBundles() + { + $bundles = array( + ... + + new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), + ); + + ... + } + +Configuration +------------- + +All features provided by the bundle are enabled by default when the bundle is +registered in your Kernel class. + +The default configuration is as follow: + +.. configuration-block:: + + .. code-block:: yaml + + sensio_framework_extra: + router: { annotations: true } + request: { converters: true } + view: { annotations: true } + cache: { annotations: true } + + .. code-block:: xml + + + + + + + + + + .. code-block:: php + + // load the profiler + $container->loadFromExtension('sensio_framework_extra', array( + 'router' => array('annotations' => true), + 'request' => array('converters' => true), + 'view' => array('annotations' => true), + 'cache' => array('annotations' => true), + )); + +You can disable some annotations and conventions by defining one or more +settings to false. + +Annotations for Controllers +--------------------------- + +Annotations are a great way to easily configure your controllers, from the +routes to the cache configuration. + +Even if annotations are not a native feature of PHP, it still has several +advantages over the classic Symfony2 configuration methods: + +* Code and configuration are in the same place (the controller class); +* Simple to learn and to use; +* Concise to write; +* Makes your Controller thin (as its sole responsibility is to get data from + the Model). + +.. tip:: + + If you use view classes, annotations are a great way to avoid creating + view classes for simple and common use cases. + +The following annotations are defined by the bundle: + +.. toctree:: + :maxdepth: 1 + + annotations/routing + annotations/converters + annotations/view + annotations/cache + +This example shows all the available annotations in action:: + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; + + /** + * @Route("/blog") + * @Cache(expires="tomorrow") + */ + class AnnotController extends Controller + { + /** + * @Route("/") + * @Template + */ + public function indexAction() + { + $posts = ...; + + return array('posts' => $posts); + } + + /** + * @Route("/{id}") + * @Method("GET") + * @ParamConverter("post", class="SensioBlogBundle:Post") + * @Template("SensioBlogBundle:Annot:post.html.twig", vars={"post"}) + * @Cache(smaxage="15") + */ + public function showAction(Post $post) + { + } + } + +As the ``showAction`` method follows some conventions, you can omit some +annotations:: + + /** + * @Route("/{id}") + * @Cache(smaxage="15") + */ + public function showAction(Post $post) + { + } + +.. _`SensioFrameworkExtraBundle`: https://github.com/sensio/SensioFrameworkExtraBundle +.. _`Download`: http://github.com/sensio/SensioFrameworkExtraBundle diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Routing/AnnotatedRouteControllerLoader.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Routing/AnnotatedRouteControllerLoader.php new file mode 100644 index 0000000..1c19900 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Routing/AnnotatedRouteControllerLoader.php @@ -0,0 +1,75 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * AnnotatedRouteControllerLoader is an implementation of AnnotationClassLoader + * that sets the '_controller' default based on the class and method names. + * + * It also parse the @Method annotation. + * + * @author Fabien Potencier + */ +class AnnotatedRouteControllerLoader extends AnnotationClassLoader +{ + /** + * Configures the _controller default parameter and eventually the _method + * requirement of a given Route instance. + * + * @param Route $route A Route instance + * @param ReflectionClass $class A ReflectionClass instance + * @param ReflectionMethod $method A ReflectionClass method + */ + protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot) + { + // controller + $classAnnot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass); + if ($classAnnot && $service = $classAnnot->getService()) { + $route->setDefault('_controller', $service.':'.$method->getName()); + } else { + $route->setDefault('_controller', $class->getName().'::'.$method->getName()); + } + + // requirements (@Method) + foreach ($this->reader->getMethodAnnotations($method) as $configuration) { + if ($configuration instanceof Method) { + $route->setRequirement('_method', implode('|', $configuration->getMethods())); + } + } + } + + /** + * Makes the default route name more sane by removing common keywords. + * + * @param ReflectionClass $class A ReflectionClass instance + * @param ReflectionMethod $method A ReflectionMethod instance + * @return string + */ + protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method) + { + $routeName = parent::getDefaultRouteName($class, $method); + + return preg_replace(array( + '/(bundle|controller)_/', + '/action(_\d+)?$/', + '/__/' + ), array( + '_', + '\\1', + '_' + ), $routeName); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/SensioFrameworkExtraBundle.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/SensioFrameworkExtraBundle.php new file mode 100644 index 0000000..2574a09 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/SensioFrameworkExtraBundle.php @@ -0,0 +1,31 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * SensioFrameworkExtraBundle. + * + * @author Fabien Potencier + */ +class SensioFrameworkExtraBundle extends Bundle +{ + public function build(ContainerBuilder $container) + { + parent::build($container); + + $container->addCompilerPass(new AddParamConverterPass()); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Templating/TemplateGuesser.php b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Templating/TemplateGuesser.php new file mode 100644 index 0000000..e2e2616 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/Templating/TemplateGuesser.php @@ -0,0 +1,98 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * The TemplateGuesser class handles the guessing of template name based on controller + * + * @author Fabien Potencier + */ +class TemplateGuesser +{ + /** + * @var Symfony\Component\HttpKernel\KernelInterface + */ + protected $kernel; + + /** + * Constructor. + * + * @param KernelInterface $kernel A KernelInterface instance + */ + public function __construct(KernelInterface $kernel) + { + $this->kernel = $kernel; + } + + /** + * Guesses and returns the template name to render based on the controller + * and action names. + * + * @param array $controller An array storing the controller object and action method + * @param Request $request A Request instance + * @param string $engine + * @return TemplateReference template reference + * @throws \InvalidArgumentException + */ + public function guessTemplateName($controller, Request $request, $engine = 'twig') + { + $className = get_class($controller[0]); + + // When JMSSecurityExtraBundle is used it generates Controller classes as MyAccountController__CG__ + if (class_exists('CG\\Core\\ClassUtils')) { + $className = ClassUtils::getUserClass($className); + } + + if (!preg_match('/Controller\\\(.+)Controller$/', $className, $matchController)) { + throw new \InvalidArgumentException(sprintf('The "%s" class does not look like a controller class (it must be in a "Controller" sub-namespace and the class name must end with "Controller")', get_class($controller[0]))); + + } + if (!preg_match('/^(.+)Action$/', $controller[1], $matchAction)) { + throw new \InvalidArgumentException(sprintf('The "%s" method does not look like an action method (it does not end with Action)', $controller[1])); + } + + $bundle = $this->getBundleForClass($className); + + return new TemplateReference($bundle->getName(), $matchController[1], $matchAction[1], $request->getRequestFormat(), $engine); + } + + /** + * Returns the Bundle instance in which the given class name is located. + * + * @param string $class A fully qualified controller class name + * @param Bundle $bundle A Bundle instance + * @throws \InvalidArgumentException + */ + protected function getBundleForClass($class) + { + $reflectionClass = new \ReflectionClass($class); + $bundles = $this->kernel->getBundles(); + + do { + $namespace = $reflectionClass->getNamespaceName(); + foreach ($bundles as $bundle) { + if (0 === strpos($namespace, $bundle->getNamespace())) { + return $bundle; + } + } + $reflectionClass = $reflectionClass->getParentClass(); + } while ($reflectionClass); + + throw new \InvalidArgumentException(sprintf('The "%s" class does not belong to a registered bundle.', $class)); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/composer.json b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/composer.json new file mode 100644 index 0000000..793388d --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/composer.json @@ -0,0 +1,26 @@ +{ + "name": "sensio/framework-extra-bundle", + "description": "This bundle provides a way to configure your controllers with annotations", + "keywords": ["annotations","controllers"], + "type": "symfony-bundle", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "symfony/framework-bundle": "2.1.*", + "doctrine/common": ">=2.1,<2.4-dev" + }, + "autoload": { + "psr-0": { "Sensio\\Bundle\\FrameworkExtraBundle": "" } + }, + "target-dir": "Sensio/Bundle/FrameworkExtraBundle", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/.travis.yml b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/.travis.yml new file mode 100644 index 0000000..96f6fb3 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/.travis.yml @@ -0,0 +1,9 @@ +language: php + +php: + - 5.3 + - 5.4 + +before_script: + - wget http://getcomposer.org/composer.phar + - php composer.phar install diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateBundleCommand.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateBundleCommand.php new file mode 100644 index 0000000..9aa79ee --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateBundleCommand.php @@ -0,0 +1,316 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Sensio\Bundle\GeneratorBundle\Generator\BundleGenerator; +use Sensio\Bundle\GeneratorBundle\Manipulator\KernelManipulator; +use Sensio\Bundle\GeneratorBundle\Manipulator\RoutingManipulator; +use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper; + +/** + * Generates bundles. + * + * @author Fabien Potencier + */ +class GenerateBundleCommand extends ContainerAwareCommand +{ + private $generator; + + /** + * @see Command + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputOption('namespace', '', InputOption::VALUE_REQUIRED, 'The namespace of the bundle to create'), + new InputOption('dir', '', InputOption::VALUE_REQUIRED, 'The directory where to create the bundle'), + new InputOption('bundle-name', '', InputOption::VALUE_REQUIRED, 'The optional bundle name'), + new InputOption('format', '', InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)', 'annotation'), + new InputOption('structure', '', InputOption::VALUE_NONE, 'Whether to generate the whole directory structure'), + )) + ->setDescription('Generates a bundle') + ->setHelp(<<generate:bundle command helps you generates new bundles. + +By default, the command interacts with the developer to tweak the generation. +Any passed option will be used as a default value for the interaction +(--namespace is the only one needed if you follow the +conventions): + +php app/console generate:bundle --namespace=Acme/BlogBundle + +Note that you can use / instead of \\ for the namespace delimiter to avoid any +problem. + +If you want to disable any user interaction, use --no-interaction but don't forget to pass all needed options: + +php app/console generate:bundle --namespace=Acme/BlogBundle --dir=src [--bundle-name=...] --no-interaction + +Note that the bundle namespace must end with "Bundle". +EOT + ) + ->setName('generate:bundle') + ; + } + + /** + * @see Command + * + * @throws \InvalidArgumentException When namespace doesn't end with Bundle + * @throws \RuntimeException When bundle can't be executed + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + + if ($input->isInteractive()) { + if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) { + $output->writeln('Command aborted'); + + return 1; + } + } + + foreach (array('namespace', 'dir') as $option) { + if (null === $input->getOption($option)) { + throw new \RuntimeException(sprintf('The "%s" option must be provided.', $option)); + } + } + + $namespace = Validators::validateBundleNamespace($input->getOption('namespace')); + if (!$bundle = $input->getOption('bundle-name')) { + $bundle = strtr($namespace, array('\\' => '')); + } + $bundle = Validators::validateBundleName($bundle); + $dir = Validators::validateTargetDir($input->getOption('dir'), $bundle, $namespace); + $format = Validators::validateFormat($input->getOption('format')); + $structure = $input->getOption('structure'); + + $dialog->writeSection($output, 'Bundle generation'); + + if (!$this->getContainer()->get('filesystem')->isAbsolutePath($dir)) { + $dir = getcwd().'/'.$dir; + } + + $generator = $this->getGenerator(); + $generator->generate($namespace, $bundle, $dir, $format, $structure); + + $output->writeln('Generating the bundle code: OK'); + + $errors = array(); + $runner = $dialog->getRunner($output, $errors); + + // check that the namespace is already autoloaded + $runner($this->checkAutoloader($output, $namespace, $bundle, $dir)); + + // register the bundle in the Kernel class + $runner($this->updateKernel($dialog, $input, $output, $this->getContainer()->get('kernel'), $namespace, $bundle)); + + // routing + $runner($this->updateRouting($dialog, $input, $output, $bundle, $format)); + + $dialog->writeGeneratorSummary($output, $errors); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + $dialog->writeSection($output, 'Welcome to the Symfony2 bundle generator'); + + // namespace + $output->writeln(array( + '', + 'Your application code must be written in bundles. This command helps', + 'you generate them easily.', + '', + 'Each bundle is hosted under a namespace (like Acme/Bundle/BlogBundle).', + 'The namespace should begin with a "vendor" name like your company name, your', + 'project name, or your client name, followed by one or more optional category', + 'sub-namespaces, and it should end with the bundle name itself', + '(which must have Bundle as a suffix).', + '', + 'See http://symfony.com/doc/current/cookbook/bundles/best_practices.html#index-1 for more', + 'details on bundle naming conventions.', + '', + 'Use / instead of \\ for the namespace delimiter to avoid any problem.', + '', + )); + + $namespace = $dialog->askAndValidate($output, $dialog->getQuestion('Bundle namespace', $input->getOption('namespace')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleNamespace'), false, $input->getOption('namespace')); + $input->setOption('namespace', $namespace); + + // bundle name + $bundle = $input->getOption('bundle-name') ?: strtr($namespace, array('\\Bundle\\' => '', '\\' => '')); + $output->writeln(array( + '', + 'In your code, a bundle is often referenced by its name. It can be the', + 'concatenation of all namespace parts but it\'s really up to you to come', + 'up with a unique name (a good practice is to start with the vendor name).', + 'Based on the namespace, we suggest '.$bundle.'.', + '', + )); + $bundle = $dialog->askAndValidate($output, $dialog->getQuestion('Bundle name', $bundle), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleName'), false, $bundle); + $input->setOption('bundle-name', $bundle); + + // target dir + $dir = $input->getOption('dir') ?: dirname($this->getContainer()->getParameter('kernel.root_dir')).'/src'; + $output->writeln(array( + '', + 'The bundle can be generated anywhere. The suggested default directory uses', + 'the standard conventions.', + '', + )); + $dir = $dialog->askAndValidate($output, $dialog->getQuestion('Target directory', $dir), function ($dir) use ($bundle, $namespace) { return Validators::validateTargetDir($dir, $bundle, $namespace); }, false, $dir); + $input->setOption('dir', $dir); + + // format + $output->writeln(array( + '', + 'Determine the format to use for the generated configuration.', + '', + )); + $format = $dialog->askAndValidate($output, $dialog->getQuestion('Configuration format (yml, xml, php, or annotation)', $input->getOption('format')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $input->getOption('format')); + $input->setOption('format', $format); + + // optional files to generate + $output->writeln(array( + '', + 'To help you get started faster, the command can generate some', + 'code snippets for you.', + '', + )); + + $structure = $input->getOption('structure'); + if (!$structure && $dialog->askConfirmation($output, $dialog->getQuestion('Do you want to generate the whole directory structure', 'no', '?'), false)) { + $structure = true; + } + $input->setOption('structure', $structure); + + // summary + $output->writeln(array( + '', + $this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true), + '', + sprintf("You are going to generate a \"%s\\%s\" bundle\nin \"%s\" using the \"%s\" format.", $namespace, $bundle, $dir, $format), + '', + )); + } + + protected function checkAutoloader(OutputInterface $output, $namespace, $bundle, $dir) + { + $output->write('Checking that the bundle is autoloaded: '); + if (!class_exists($namespace.'\\'.$bundle)) { + return array( + '- Edit the composer.json file and register the bundle', + ' namespace in the "autoload" section:', + '', + ); + } + } + + protected function updateKernel($dialog, InputInterface $input, OutputInterface $output, KernelInterface $kernel, $namespace, $bundle) + { + $auto = true; + if ($input->isInteractive()) { + $auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of your Kernel', 'yes', '?'), true); + } + + $output->write('Enabling the bundle inside the Kernel: '); + $manip = new KernelManipulator($kernel); + try { + $ret = $auto ? $manip->addBundle($namespace.'\\'.$bundle) : false; + + if (!$ret) { + $reflected = new \ReflectionObject($kernel); + + return array( + sprintf('- Edit %s', $reflected->getFilename()), + ' and add the following bundle in the AppKernel::registerBundles() method:', + '', + sprintf(' new %s(),', $namespace.'\\'.$bundle), + '', + ); + } + } catch (\RuntimeException $e) { + return array( + sprintf('Bundle %s is already defined in AppKernel::registerBundles().', $namespace.'\\'.$bundle), + '', + ); + } + } + + protected function updateRouting($dialog, InputInterface $input, OutputInterface $output, $bundle, $format) + { + $auto = true; + if ($input->isInteractive()) { + $auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true); + } + + $output->write('Importing the bundle routing resource: '); + $routing = new RoutingManipulator($this->getContainer()->getParameter('kernel.root_dir').'/config/routing.yml'); + try { + $ret = $auto ? $routing->addResource($bundle, $format) : false; + if (!$ret) { + if ('annotation' === $format) { + $help = sprintf(" resource: \"@%s/Controller/\"\n type: annotation\n", $bundle); + } else { + $help = sprintf(" resource: \"@%s/Resources/config/routing.%s\"\n", $bundle, $format); + } + $help .= " prefix: /\n"; + + return array( + '- Import the bundle\'s routing resource in the app main routing file:', + '', + sprintf(' %s:', $bundle), + $help, + '', + ); + } + } catch (\RuntimeException $e) { + return array( + sprintf('Bundle %s is already imported.', $bundle), + '', + ); + } + } + + protected function getGenerator() + { + if (null === $this->generator) { + $this->generator = new BundleGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/bundle'); + } + + return $this->generator; + } + + public function setGenerator(BundleGenerator $generator) + { + $this->generator = $generator; + } + + protected function getDialogHelper() + { + $dialog = $this->getHelperSet()->get('dialog'); + if (!$dialog || get_class($dialog) !== 'Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper') { + $this->getHelperSet()->set($dialog = new DialogHelper()); + } + + return $dialog; + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineCommand.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineCommand.php new file mode 100644 index 0000000..6c784f5 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineCommand.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +use Doctrine\Bundle\DoctrineBundle\Mapping\MetadataFactory; +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; + +abstract class GenerateDoctrineCommand extends ContainerAwareCommand +{ + public function isEnabled() + { + return class_exists('Doctrine\\Bundle\\DoctrineBundle\\DoctrineBundle'); + } + + protected function parseShortcutNotation($shortcut) + { + $entity = str_replace('/', '\\', $shortcut); + + if (false === $pos = strpos($entity, ':')) { + throw new \InvalidArgumentException(sprintf('The entity name must contain a : ("%s" given, expecting something like AcmeBlogBundle:Blog/Post)', $entity)); + } + + return array(substr($entity, 0, $pos), substr($entity, $pos + 1)); + } + + protected function getEntityMetadata($entity) + { + $factory = new MetadataFactory($this->getContainer()->get('doctrine')); + + return $factory->getClassMetadata($entity)->getMetadata(); + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineCrudCommand.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineCrudCommand.php new file mode 100644 index 0000000..dfe049a --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineCrudCommand.php @@ -0,0 +1,274 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Command\Command; +use Sensio\Bundle\GeneratorBundle\Generator\DoctrineCrudGenerator; +use Sensio\Bundle\GeneratorBundle\Generator\DoctrineFormGenerator; +use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper; +use Sensio\Bundle\GeneratorBundle\Manipulator\RoutingManipulator; + +/** + * Generates a CRUD for a Doctrine entity. + * + * @author Fabien Potencier + */ +class GenerateDoctrineCrudCommand extends GenerateDoctrineCommand +{ + private $generator; + private $formGenerator; + + /** + * @see Command + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputOption('entity', '', InputOption::VALUE_REQUIRED, 'The entity class name to initialize (shortcut notation)'), + new InputOption('route-prefix', '', InputOption::VALUE_REQUIRED, 'The route prefix'), + new InputOption('with-write', '', InputOption::VALUE_NONE, 'Whether or not to generate create, new and delete actions'), + new InputOption('format', '', InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)', 'annotation'), + )) + ->setDescription('Generates a CRUD based on a Doctrine entity') + ->setHelp(<<doctrine:generate:crud command generates a CRUD based on a Doctrine entity. + +The default command only generates the list and show actions. + +php app/console doctrine:generate:crud --entity=AcmeBlogBundle:Post --route-prefix=post_admin + +Using the --with-write option allows to generate the new, edit and delete actions. + +php app/console doctrine:generate:crud --entity=AcmeBlogBundle:Post --route-prefix=post_admin --with-write +EOT + ) + ->setName('doctrine:generate:crud') + ->setAliases(array('generate:doctrine:crud')) + ; + } + + /** + * @see Command + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + + if ($input->isInteractive()) { + if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) { + $output->writeln('Command aborted'); + + return 1; + } + } + + $entity = Validators::validateEntityName($input->getOption('entity')); + list($bundle, $entity) = $this->parseShortcutNotation($entity); + + $format = Validators::validateFormat($input->getOption('format')); + $prefix = $this->getRoutePrefix($input, $entity); + $withWrite = $input->getOption('with-write'); + + $dialog->writeSection($output, 'CRUD generation'); + + $entityClass = $this->getContainer()->get('doctrine')->getEntityNamespace($bundle).'\\'.$entity; + $metadata = $this->getEntityMetadata($entityClass); + $bundle = $this->getContainer()->get('kernel')->getBundle($bundle); + + $generator = $this->getGenerator(); + $generator->generate($bundle, $entity, $metadata[0], $format, $prefix, $withWrite); + + $output->writeln('Generating the CRUD code: OK'); + + $errors = array(); + $runner = $dialog->getRunner($output, $errors); + + // form + if ($withWrite) { + $this->generateForm($bundle, $entity, $metadata); + $output->writeln('Generating the Form code: OK'); + } + + // routing + if ('annotation' != $format) { + $runner($this->updateRouting($dialog, $input, $output, $bundle, $format, $entity, $prefix)); + } + + $dialog->writeGeneratorSummary($output, $errors); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + $dialog->writeSection($output, 'Welcome to the Doctrine2 CRUD generator'); + + // namespace + $output->writeln(array( + '', + 'This command helps you generate CRUD controllers and templates.', + '', + 'First, you need to give the entity for which you want to generate a CRUD.', + 'You can give an entity that does not exist yet and the wizard will help', + 'you defining it.', + '', + 'You must use the shortcut notation like AcmeBlogBundle:Post.', + '', + )); + + $entity = $dialog->askAndValidate($output, $dialog->getQuestion('The Entity shortcut name', $input->getOption('entity')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateEntityName'), false, $input->getOption('entity')); + $input->setOption('entity', $entity); + list($bundle, $entity) = $this->parseShortcutNotation($entity); + + // Entity exists? + $entityClass = $this->getContainer()->get('doctrine')->getEntityNamespace($bundle).'\\'.$entity; + $metadata = $this->getEntityMetadata($entityClass); + + // write? + $withWrite = $input->getOption('with-write') ?: false; + $output->writeln(array( + '', + 'By default, the generator creates two actions: list and show.', + 'You can also ask it to generate "write" actions: new, update, and delete.', + '', + )); + $withWrite = $dialog->askConfirmation($output, $dialog->getQuestion('Do you want to generate the "write" actions', $withWrite ? 'yes' : 'no', '?'), $withWrite); + $input->setOption('with-write', $withWrite); + + // format + $format = $input->getOption('format'); + $output->writeln(array( + '', + 'Determine the format to use for the generated CRUD.', + '', + )); + $format = $dialog->askAndValidate($output, $dialog->getQuestion('Configuration format (yml, xml, php, or annotation)', $format), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $format); + $input->setOption('format', $format); + + // route prefix + $prefix = $this->getRoutePrefix($input, $entity); + $output->writeln(array( + '', + 'Determine the routes prefix (all the routes will be "mounted" under this', + 'prefix: /prefix/, /prefix/new, ...).', + '', + )); + $prefix = $dialog->ask($output, $dialog->getQuestion('Routes prefix', '/'.$prefix), '/'.$prefix); + $input->setOption('route-prefix', $prefix); + + // summary + $output->writeln(array( + '', + $this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true), + '', + sprintf("You are going to generate a CRUD controller for \"%s:%s\"", $bundle, $entity), + sprintf("using the \"%s\" format.", $format), + '', + )); + } + + /** + * Tries to generate forms if they don't exist yet and if we need write operations on entities. + */ + private function generateForm($bundle, $entity, $metadata) + { + try { + $this->getFormGenerator()->generate($bundle, $entity, $metadata[0]); + } catch (\RuntimeException $e ) { + // form already exists + } + } + + private function updateRouting($dialog, InputInterface $input, OutputInterface $output, $bundle, $format, $entity, $prefix) + { + $auto = true; + if ($input->isInteractive()) { + $auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true); + } + + $output->write('Importing the CRUD routes: '); + $this->getContainer()->get('filesystem')->mkdir($bundle->getPath().'/Resources/config/'); + $routing = new RoutingManipulator($bundle->getPath().'/Resources/config/routing.yml'); + try { + $ret = $auto ? $routing->addResource($bundle->getName(), $format, '/'.$prefix, 'routing/'.strtolower(str_replace('\\', '_', $entity))) : false; + } catch (\RuntimeException $exc) { + $ret = false; + } + + if (!$ret) { + $help = sprintf(" resource: \"@%s/Resources/config/routing/%s.%s\"\n", $bundle->getName(), strtolower(str_replace('\\', '_', $entity)), $format); + $help .= sprintf(" prefix: /%s\n", $prefix); + + return array( + '- Import the bundle\'s routing resource in the bundle routing file', + sprintf(' (%s).', $bundle->getPath().'/Resources/config/routing.yml'), + '', + sprintf(' %s:', $bundle->getName().('' !== $prefix ? '_'.str_replace('/', '_', $prefix) : '')), + $help, + '', + ); + } + } + + protected function getRoutePrefix(InputInterface $input, $entity) + { + $prefix = $input->getOption('route-prefix') ?: strtolower(str_replace(array('\\', '/'), '_', $entity)); + + if ($prefix && '/' === $prefix[0]) { + $prefix = substr($prefix, 1); + } + + return $prefix; + } + + protected function getGenerator() + { + if (null === $this->generator) { + $this->generator = new DoctrineCrudGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/crud'); + } + + return $this->generator; + } + + public function setGenerator(DoctrineCrudGenerator $generator) + { + $this->generator = $generator; + } + + protected function getFormGenerator() + { + if (null === $this->formGenerator) { + $this->formGenerator = new DoctrineFormGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/form'); + } + + return $this->formGenerator; + } + + public function setFormGenerator(DoctrineFormGenerator $formGenerator) + { + $this->formGenerator = $formGenerator; + } + + protected function getDialogHelper() + { + $dialog = $this->getHelperSet()->get('dialog'); + if (!$dialog || get_class($dialog) !== 'Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper') { + $this->getHelperSet()->set($dialog = new DialogHelper()); + } + + return $dialog; + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineEntityCommand.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineEntityCommand.php new file mode 100644 index 0000000..5e0c89e --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineEntityCommand.php @@ -0,0 +1,312 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +use Sensio\Bundle\GeneratorBundle\Generator\DoctrineEntityGenerator; +use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\DBAL\Types\Type; + +/** + * Initializes a Doctrine entity inside a bundle. + * + * @author Fabien Potencier + */ +class GenerateDoctrineEntityCommand extends GenerateDoctrineCommand +{ + private $generator; + + protected function configure() + { + $this + ->setName('doctrine:generate:entity') + ->setAliases(array('generate:doctrine:entity')) + ->setDescription('Generates a new Doctrine entity inside a bundle') + ->addOption('entity', null, InputOption::VALUE_REQUIRED, 'The entity class name to initialize (shortcut notation)') + ->addOption('fields', null, InputOption::VALUE_REQUIRED, 'The fields to create with the new entity') + ->addOption('format', null, InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)', 'annotation') + ->addOption('with-repository', null, InputOption::VALUE_NONE, 'Whether to generate the entity repository or not') + ->setHelp(<<doctrine:generate:entity task generates a new Doctrine +entity inside a bundle: + +php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post + +The above command would initialize a new entity in the following entity +namespace Acme\BlogBundle\Entity\Blog\Post. + +You can also optionally specify the fields you want to generate in the new +entity: + +php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --fields="title:string(255) body:text" + +The command can also generate the corresponding entity repository class with the +--with-repository option: + +php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --with-repository + +By default, the command uses annotations for the mapping information; change it +with --format: + +php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --format=yml + +To deactivate the interaction mode, simply use the `--no-interaction` option +without forgetting to pass all needed options: + +php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --format=annotation --fields="title:string(255) body:text" --with-repository --no-interaction +EOT + ); + } + + /** + * @throws \InvalidArgumentException When the bundle doesn't end with Bundle (Example: "Bundle/MySampleBundle") + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + + if ($input->isInteractive()) { + if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) { + $output->writeln('Command aborted'); + + return 1; + } + } + + $entity = Validators::validateEntityName($input->getOption('entity')); + list($bundle, $entity) = $this->parseShortcutNotation($entity); + $format = Validators::validateFormat($input->getOption('format')); + $fields = $this->parseFields($input->getOption('fields')); + + $dialog->writeSection($output, 'Entity generation'); + + $bundle = $this->getContainer()->get('kernel')->getBundle($bundle); + + $generator = $this->getGenerator(); + $generator->generate($bundle, $entity, $format, array_values($fields), $input->getOption('with-repository')); + + $output->writeln('Generating the entity code: OK'); + + $dialog->writeGeneratorSummary($output, array()); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + $dialog->writeSection($output, 'Welcome to the Doctrine2 entity generator'); + + // namespace + $output->writeln(array( + '', + 'This command helps you generate Doctrine2 entities.', + '', + 'First, you need to give the entity name you want to generate.', + 'You must use the shortcut notation like AcmeBlogBundle:Post.', + '' + )); + + while (true) { + $entity = $dialog->askAndValidate($output, $dialog->getQuestion('The Entity shortcut name', $input->getOption('entity')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateEntityName'), false, $input->getOption('entity')); + + list($bundle, $entity) = $this->parseShortcutNotation($entity); + + // check reserved words + if ($this->getGenerator()->isReservedKeyword($entity)){ + $output->writeln(sprintf(' "%s" is a reserved word.', $entity)); + continue; + } + + try { + $b = $this->getContainer()->get('kernel')->getBundle($bundle); + + if (!file_exists($b->getPath().'/Entity/'.str_replace('\\', '/', $entity).'.php')) { + break; + } + + $output->writeln(sprintf('Entity "%s:%s" already exists.', $bundle, $entity)); + } catch (\Exception $e) { + $output->writeln(sprintf('Bundle "%s" does not exist.', $bundle)); + } + } + $input->setOption('entity', $bundle.':'.$entity); + + // format + $output->writeln(array( + '', + 'Determine the format to use for the mapping information.', + '', + )); + $format = $dialog->askAndValidate($output, $dialog->getQuestion('Configuration format (yml, xml, php, or annotation)', $input->getOption('format')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $input->getOption('format')); + $input->setOption('format', $format); + + // fields + $input->setOption('fields', $this->addFields($input, $output, $dialog)); + + // repository? + $output->writeln(''); + $withRepository = $dialog->askConfirmation($output, $dialog->getQuestion('Do you want to generate an empty repository class', $input->getOption('with-repository') ? 'yes' : 'no', '?'), $input->getOption('with-repository')); + $input->setOption('with-repository', $withRepository); + + // summary + $output->writeln(array( + '', + $this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true), + '', + sprintf("You are going to generate a \"%s:%s\" Doctrine2 entity", $bundle, $entity), + sprintf("using the \"%s\" format.", $format), + '', + )); + } + + private function parseFields($input) + { + if (is_array($input)) { + return $input; + } + + $fields = array(); + foreach (explode(' ', $input) as $value) { + $elements = explode(':', $value); + $name = $elements[0]; + if (strlen($name)) { + $type = isset($elements[1]) ? $elements[1] : 'string'; + preg_match_all('/(.*)\((.*)\)/', $type, $matches); + $type = isset($matches[1][0]) ? $matches[1][0] : $type; + $length = isset($matches[2][0]) ? $matches[2][0] : null; + + $fields[$name] = array('fieldName' => $name, 'type' => $type, 'length' => $length); + } + } + + return $fields; + } + + private function addFields(InputInterface $input, OutputInterface $output, DialogHelper $dialog) + { + $fields = $this->parseFields($input->getOption('fields')); + $output->writeln(array( + '', + 'Instead of starting with a blank entity, you can add some fields now.', + 'Note that the primary key will be added automatically (named id).', + '', + )); + $output->write('Available types: '); + + $types = array_keys(Type::getTypesMap()); + $count = 20; + foreach ($types as $i => $type) { + if ($count > 50) { + $count = 0; + $output->writeln(''); + } + $count += strlen($type); + $output->write(sprintf('%s', $type)); + if (count($types) != $i + 1) { + $output->write(', '); + } else { + $output->write('.'); + } + } + $output->writeln(''); + + $fieldValidator = function ($type) use ($types) { + // FIXME: take into account user-defined field types + if (!in_array($type, $types)) { + throw new \InvalidArgumentException(sprintf('Invalid type "%s".', $type)); + } + + return $type; + }; + + $lengthValidator = function ($length) { + if (!$length) { + return $length; + } + + $result = filter_var($length, FILTER_VALIDATE_INT, array( + 'options' => array('min_range' => 1) + )); + + if (false === $result) { + throw new \InvalidArgumentException(sprintf('Invalid length "%s".', $length)); + } + + return $length; + }; + + while (true) { + $output->writeln(''); + $self = $this; + $name = $dialog->askAndValidate($output, $dialog->getQuestion('New field name (press to stop adding fields)', null), function ($name) use ($fields, $self) { + if (isset($fields[$name]) || 'id' == $name) { + throw new \InvalidArgumentException(sprintf('Field "%s" is already defined.', $name)); + } + + // check reserved words + if ($self->getGenerator()->isReservedKeyword($name)){ + throw new \InvalidArgumentException(sprintf('Name "%s" is a reserved word.', $name)); + } + + return $name; + }); + if (!$name) { + break; + } + + $defaultType = 'string'; + + if (substr($name, -3) == '_at') { + $defaultType = 'datetime'; + } elseif (substr($name, -3) == '_id') { + $defaultType = 'integer'; + } + + $type = $dialog->askAndValidate($output, $dialog->getQuestion('Field type', $defaultType), $fieldValidator, false, $defaultType); + + $data = array('fieldName' => $name, 'type' => $type); + + if ($type == 'string') { + $data['length'] = $dialog->askAndValidate($output, $dialog->getQuestion('Field length', 255), $lengthValidator, false, 255); + } + + $fields[$name] = $data; + } + + return $fields; + } + + public function getGenerator() + { + if (null === $this->generator) { + $this->generator = new DoctrineEntityGenerator($this->getContainer()->get('filesystem'), $this->getContainer()->get('doctrine')); + } + + return $this->generator; + } + + public function setGenerator(DoctrineEntityGenerator $generator) + { + $this->generator = $generator; + } + + protected function getDialogHelper() + { + $dialog = $this->getHelperSet()->get('dialog'); + if (!$dialog || get_class($dialog) !== 'Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper') { + $this->getHelperSet()->set($dialog = new DialogHelper()); + } + + return $dialog; + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineFormCommand.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineFormCommand.php new file mode 100644 index 0000000..412b46a --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/GenerateDoctrineFormCommand.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Command\Command; +use Sensio\Bundle\GeneratorBundle\Generator\DoctrineFormGenerator; + +/** + * Generates a form type class for a given Doctrine entity. + * + * @author Fabien Potencier + * @author Hugo Hamon + */ +class GenerateDoctrineFormCommand extends GenerateDoctrineCommand +{ + /** + * @see Command + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('entity', InputArgument::REQUIRED, 'The entity class name to initialize (shortcut notation)'), + )) + ->setDescription('Generates a form type class based on a Doctrine entity') + ->setHelp(<<doctrine:generate:form command generates a form class based on a Doctrine entity. + +php app/console doctrine:generate:form AcmeBlogBundle:Post +EOT + ) + ->setName('doctrine:generate:form') + ->setAliases(array('generate:doctrine:form')) + ; + } + + /** + * @see Command + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $entity = Validators::validateEntityName($input->getArgument('entity')); + list($bundle, $entity) = $this->parseShortcutNotation($entity); + + $entityClass = $this->getContainer()->get('doctrine')->getEntityNamespace($bundle).'\\'.$entity; + $metadata = $this->getEntityMetadata($entityClass); + $bundle = $this->getApplication()->getKernel()->getBundle($bundle); + + $generator = new DoctrineFormGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/form'); + $generator->generate($bundle, $entity, $metadata[0]); + + $output->writeln(sprintf( + 'The new %s.php class file has been created under %s.', + $generator->getClassName(), + $generator->getClassPath() + )); + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/Helper/DialogHelper.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/Helper/DialogHelper.php new file mode 100644 index 0000000..c0d8c10 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/Helper/DialogHelper.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command\Helper; + +use Symfony\Component\Console\Helper\DialogHelper as BaseDialogHelper; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Generates bundles. + * + * @author Fabien Potencier + */ +class DialogHelper extends BaseDialogHelper +{ + public function writeGeneratorSummary(OutputInterface $output, $errors) + { + if (!$errors) { + $this->writeSection($output, 'You can now start using the generated code!'); + } else { + $this->writeSection($output, array( + 'The command was not able to configure everything automatically.', + 'You must do the following changes manually.', + ), 'error'); + + $output->writeln($errors); + } + } + + public function getRunner(OutputInterface $output, &$errors) + { + $runner = function ($err) use ($output, &$errors) { + if ($err) { + $output->writeln('FAILED'); + $errors = array_merge($errors, $err); + } else { + $output->writeln('OK'); + } + }; + + return $runner; + } + + public function writeSection(OutputInterface $output, $text, $style = 'bg=blue;fg=white') + { + $output->writeln(array( + '', + $this->getHelperSet()->get('formatter')->formatBlock($text, $style, true), + '', + )); + } + + public function getQuestion($question, $default, $sep = ':') + { + return $default ? sprintf('%s [%s]%s ', $question, $default, $sep) : sprintf('%s%s ', $question, $sep); + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/Validators.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/Validators.php new file mode 100644 index 0000000..7904da2 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Command/Validators.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +/** + * Validator functions. + * + * @author Fabien Potencier + */ +class Validators +{ + static public function validateBundleNamespace($namespace) + { + if (!preg_match('/Bundle$/', $namespace)) { + throw new \InvalidArgumentException('The namespace must end with Bundle.'); + } + + $namespace = strtr($namespace, '/', '\\'); + if (!preg_match('/^(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\\\?)+$/', $namespace)) { + throw new \InvalidArgumentException('The namespace contains invalid characters.'); + } + + // validate reserved keywords + $reserved = self::getReservedWords(); + foreach (explode('\\', $namespace) as $word) { + if (in_array(strtolower($word), $reserved)) { + throw new \InvalidArgumentException(sprintf('The namespace cannot contain PHP reserved words ("%s").', $word)); + } + } + + // validate that the namespace is at least one level deep + if (false === strpos($namespace, '\\')) { + $msg = array(); + $msg[] = sprintf('The namespace must contain a vendor namespace (e.g. "VendorName\%s" instead of simply "%s").', $namespace, $namespace); + $msg[] = 'If you\'ve specified a vendor namespace, did you forget to surround it with quotes (init:bundle "Acme\BlogBundle")?'; + + throw new \InvalidArgumentException(implode("\n\n", $msg)); + } + + return $namespace; + } + + static public function validateBundleName($bundle) + { + if (!preg_match('/Bundle$/', $bundle)) { + throw new \InvalidArgumentException('The bundle name must end with Bundle.'); + } + + return $bundle; + } + + static public function validateTargetDir($dir, $bundle, $namespace) + { + // add trailing / if necessary + return '/' === substr($dir, -1, 1) ? $dir : $dir.'/'; + } + + static public function validateFormat($format) + { + $format = strtolower($format); + + if (!in_array($format, array('php', 'xml', 'yml', 'annotation'))) { + throw new \RuntimeException(sprintf('Format "%s" is not supported.', $format)); + } + + return $format; + } + + static public function validateEntityName($entity) + { + if (false === $pos = strpos($entity, ':')) { + throw new \InvalidArgumentException(sprintf('The entity name must contain a : ("%s" given, expecting something like AcmeBlogBundle:Blog/Post)', $entity)); + } + + return $entity; + } + + static public function getReservedWords() + { + return array( + 'abstract', + 'and', + 'array', + 'as', + 'break', + 'case', + 'catch', + 'class', + 'clone', + 'const', + 'continue', + 'declare', + 'default', + 'do', + 'else', + 'elseif', + 'enddeclare', + 'endfor', + 'endforeach', + 'endif', + 'endswitch', + 'endwhile', + 'extends', + 'final', + 'for', + 'foreach', + 'function', + 'global', + 'goto', + 'if', + 'implements', + 'interface', + 'instanceof', + 'namespace', + 'new', + 'or', + 'private', + 'protected', + 'public', + 'static', + 'switch', + 'throw', + 'try', + 'use', + 'var', + 'while', + 'xor', + '__CLASS__', + '__DIR__', + '__FILE__', + '__LINE__', + '__FUNCTION__', + '__METHOD__', + '__NAMESPACE__', + 'die', + 'echo', + 'empty', + 'exit', + 'eval', + 'include', + 'include_once', + 'isset', + 'list', + 'require', + 'require_once', + 'return', + 'print', + 'unset', + ); + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/BundleGenerator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/BundleGenerator.php new file mode 100644 index 0000000..049cdec --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/BundleGenerator.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Generator; + +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\DependencyInjection\Container; + +/** + * Generates a bundle. + * + * @author Fabien Potencier + */ +class BundleGenerator extends Generator +{ + private $filesystem; + private $skeletonDir; + + public function __construct(Filesystem $filesystem, $skeletonDir) + { + $this->filesystem = $filesystem; + $this->skeletonDir = $skeletonDir; + } + + public function generate($namespace, $bundle, $dir, $format, $structure) + { + $dir .= '/'.strtr($namespace, '\\', '/'); + if (file_exists($dir)) { + throw new \RuntimeException(sprintf('Unable to generate the bundle as the target directory "%s" is not empty.', realpath($dir))); + } + + $basename = substr($bundle, 0, -6); + $parameters = array( + 'namespace' => $namespace, + 'bundle' => $bundle, + 'format' => $format, + 'bundle_basename' => $basename, + 'extension_alias' => Container::underscore($basename), + ); + + $this->renderFile($this->skeletonDir, 'Bundle.php', $dir.'/'.$bundle.'.php', $parameters); + $this->renderFile($this->skeletonDir, 'Extension.php', $dir.'/DependencyInjection/'.$basename.'Extension.php', $parameters); + $this->renderFile($this->skeletonDir, 'Configuration.php', $dir.'/DependencyInjection/Configuration.php', $parameters); + $this->renderFile($this->skeletonDir, 'DefaultController.php', $dir.'/Controller/DefaultController.php', $parameters); + $this->renderFile($this->skeletonDir, 'DefaultControllerTest.php', $dir.'/Tests/Controller/DefaultControllerTest.php', $parameters); + $this->renderFile($this->skeletonDir, 'index.html.twig', $dir.'/Resources/views/Default/index.html.twig', $parameters); + + if ('xml' === $format || 'annotation' === $format) { + $this->renderFile($this->skeletonDir, 'services.xml', $dir.'/Resources/config/services.xml', $parameters); + } else { + $this->renderFile($this->skeletonDir, 'services.'.$format, $dir.'/Resources/config/services.'.$format, $parameters); + } + + if ('annotation' != $format) { + $this->renderFile($this->skeletonDir, 'routing.'.$format, $dir.'/Resources/config/routing.'.$format, $parameters); + } + + if ($structure) { + $this->filesystem->mkdir($dir.'/Resources/doc'); + $this->filesystem->touch($dir.'/Resources/doc/index.rst'); + $this->filesystem->mkdir($dir.'/Resources/translations'); + $this->filesystem->copy($this->skeletonDir.'/messages.fr.xlf', $dir.'/Resources/translations/messages.fr.xlf'); + $this->filesystem->mkdir($dir.'/Resources/public/css'); + $this->filesystem->mkdir($dir.'/Resources/public/images'); + $this->filesystem->mkdir($dir.'/Resources/public/js'); + } + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineCrudGenerator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineCrudGenerator.php new file mode 100644 index 0000000..72eb9aa --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineCrudGenerator.php @@ -0,0 +1,290 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Generator; + +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * Generates a CRUD controller. + * + * @author Fabien Potencier + */ +class DoctrineCrudGenerator extends Generator +{ + protected $filesystem; + protected $skeletonDir; + protected $routePrefix; + protected $routeNamePrefix; + protected $bundle; + protected $entity; + protected $metadata; + protected $format; + protected $actions; + + /** + * Constructor. + * + * @param Filesystem $filesystem A Filesystem instance + * @param string $skeletonDir Path to the skeleton directory + */ + public function __construct(Filesystem $filesystem, $skeletonDir) + { + $this->filesystem = $filesystem; + $this->skeletonDir = $skeletonDir; + } + + /** + * Generate the CRUD controller. + * + * @param BundleInterface $bundle A bundle object + * @param string $entity The entity relative class name + * @param ClassMetadataInfo $metadata The entity class metadata + * @param string $format The configuration format (xml, yaml, annotation) + * @param string $routePrefix The route name prefix + * @param array $needWriteActions Wether or not to generate write actions + * + * @throws \RuntimeException + */ + public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata, $format, $routePrefix, $needWriteActions) + { + $this->routePrefix = $routePrefix; + $this->routeNamePrefix = str_replace('/', '_', $routePrefix); + $this->actions = $needWriteActions ? array('index', 'show', 'new', 'edit', 'delete') : array('index', 'show'); + + if (count($metadata->identifier) > 1) { + throw new \RuntimeException('The CRUD generator does not support entity classes with multiple primary keys.'); + } + + if (!in_array('id', $metadata->identifier)) { + throw new \RuntimeException('The CRUD generator expects the entity object has a primary key field named "id" with a getId() method.'); + } + + $this->entity = $entity; + $this->bundle = $bundle; + $this->metadata = $metadata; + $this->setFormat($format); + + $this->generateControllerClass(); + + $dir = sprintf('%s/Resources/views/%s', $this->bundle->getPath(), str_replace('\\', '/', $this->entity)); + + if (!file_exists($dir)) { + $this->filesystem->mkdir($dir, 0777); + } + + $this->generateIndexView($dir); + + if (in_array('show', $this->actions)) { + $this->generateShowView($dir); + } + + if (in_array('new', $this->actions)) { + $this->generateNewView($dir); + } + + if (in_array('edit', $this->actions)) { + $this->generateEditView($dir); + } + + $this->generateTestClass(); + $this->generateConfiguration(); + } + + /** + * Sets the configuration format. + * + * @param string $format The configuration format + */ + private function setFormat($format) + { + switch ($format) { + case 'yml': + case 'xml': + case 'php': + case 'annotation': + $this->format = $format; + break; + default: + $this->format = 'yml'; + break; + } + } + + /** + * Generates the routing configuration. + * + */ + private function generateConfiguration() + { + if (!in_array($this->format, array('yml', 'xml', 'php'))) { + return; + } + + $target = sprintf( + '%s/Resources/config/routing/%s.%s', + $this->bundle->getPath(), + strtolower(str_replace('\\', '_', $this->entity)), + $this->format + ); + + $this->renderFile($this->skeletonDir, 'config/routing.'.$this->format, $target, array( + 'actions' => $this->actions, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + )); + } + + /** + * Generates the controller class only. + * + */ + private function generateControllerClass() + { + $dir = $this->bundle->getPath(); + + $parts = explode('\\', $this->entity); + $entityClass = array_pop($parts); + $entityNamespace = implode('\\', $parts); + + $target = sprintf( + '%s/Controller/%s/%sController.php', + $dir, + str_replace('\\', '/', $entityNamespace), + $entityClass + ); + + if (file_exists($target)) { + throw new \RuntimeException('Unable to generate the controller as it already exists.'); + } + + $this->renderFile($this->skeletonDir, 'controller.php', $target, array( + 'actions' => $this->actions, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'dir' => $this->skeletonDir, + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + 'entity_class' => $entityClass, + 'namespace' => $this->bundle->getNamespace(), + 'entity_namespace' => $entityNamespace, + 'format' => $this->format, + )); + } + + /** + * Generates the functional test class only. + * + */ + private function generateTestClass() + { + $parts = explode('\\', $this->entity); + $entityClass = array_pop($parts); + $entityNamespace = implode('\\', $parts); + + $dir = $this->bundle->getPath() .'/Tests/Controller'; + $target = $dir .'/'. str_replace('\\', '/', $entityNamespace).'/'. $entityClass .'ControllerTest.php'; + + $this->renderFile($this->skeletonDir, 'tests/test.php', $target, array( + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'entity' => $this->entity, + 'entity_class' => $entityClass, + 'namespace' => $this->bundle->getNamespace(), + 'entity_namespace' => $entityNamespace, + 'actions' => $this->actions, + 'dir' => $this->skeletonDir, + )); + } + + /** + * Generates the index.html.twig template in the final bundle. + * + * @param string $dir The path to the folder that hosts templates in the bundle + */ + private function generateIndexView($dir) + { + $this->renderFile($this->skeletonDir, 'views/index.html.twig', $dir.'/index.html.twig', array( + 'dir' => $this->skeletonDir, + 'entity' => $this->entity, + 'fields' => $this->metadata->fieldMappings, + 'actions' => $this->actions, + 'record_actions' => $this->getRecordActions(), + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + )); + } + + /** + * Generates the show.html.twig template in the final bundle. + * + * @param string $dir The path to the folder that hosts templates in the bundle + */ + private function generateShowView($dir) + { + $this->renderFile($this->skeletonDir, 'views/show.html.twig', $dir.'/show.html.twig', array( + 'dir' => $this->skeletonDir, + 'entity' => $this->entity, + 'fields' => $this->metadata->fieldMappings, + 'actions' => $this->actions, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + )); + } + + /** + * Generates the new.html.twig template in the final bundle. + * + * @param string $dir The path to the folder that hosts templates in the bundle + */ + private function generateNewView($dir) + { + $this->renderFile($this->skeletonDir, 'views/new.html.twig', $dir.'/new.html.twig', array( + 'dir' => $this->skeletonDir, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'entity' => $this->entity, + 'actions' => $this->actions, + )); + } + + /** + * Generates the edit.html.twig template in the final bundle. + * + * @param string $dir The path to the folder that hosts templates in the bundle + */ + private function generateEditView($dir) + { + $this->renderFile($this->skeletonDir, 'views/edit.html.twig', $dir.'/edit.html.twig', array( + 'dir' => $this->skeletonDir, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'entity' => $this->entity, + 'actions' => $this->actions, + )); + } + + /** + * Returns an array of record actions to generate (edit, show). + * + * @return array + */ + private function getRecordActions() + { + return array_filter($this->actions, function($item) { + return in_array($item, array('show', 'edit')); + }); + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineEntityGenerator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineEntityGenerator.php new file mode 100644 index 0000000..cc050eb --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineEntityGenerator.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Generator; + +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Bridge\Doctrine\RegistryInterface; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Tools\EntityGenerator; +use Doctrine\ORM\Tools\EntityRepositoryGenerator; +use Doctrine\ORM\Tools\Export\ClassMetadataExporter; + +/** + * Generates a form class based on a Doctrine entity. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class DoctrineEntityGenerator extends Generator +{ + private $filesystem; + private $registry; + + public function __construct(Filesystem $filesystem, RegistryInterface $registry) + { + $this->filesystem = $filesystem; + $this->registry = $registry; + } + + public function generate(BundleInterface $bundle, $entity, $format, array $fields, $withRepository) + { + // configure the bundle (needed if the bundle does not contain any Entities yet) + $config = $this->registry->getEntityManager(null)->getConfiguration(); + $config->setEntityNamespaces(array_merge( + array($bundle->getName() => $bundle->getNamespace().'\\Entity'), + $config->getEntityNamespaces() + )); + + $entityClass = $this->registry->getEntityNamespace($bundle->getName()).'\\'.$entity; + $entityPath = $bundle->getPath().'/Entity/'.str_replace('\\', '/', $entity).'.php'; + if (file_exists($entityPath)) { + throw new \RuntimeException(sprintf('Entity "%s" already exists.', $entityClass)); + } + + $class = new ClassMetadataInfo($entityClass); + if ($withRepository) { + $class->customRepositoryClassName = $entityClass.'Repository'; + } + $class->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true)); + $class->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + foreach ($fields as $field) { + $class->mapField($field); + } + + $entityGenerator = $this->getEntityGenerator(); + if ('annotation' === $format) { + $entityGenerator->setGenerateAnnotations(true); + $entityCode = $entityGenerator->generateEntityClass($class); + $mappingPath = $mappingCode = false; + } else { + $cme = new ClassMetadataExporter(); + $exporter = $cme->getExporter('yml' == $format ? 'yaml' : $format); + $mappingPath = $bundle->getPath().'/Resources/config/doctrine/'.str_replace('\\', '.', $entity).'.orm.'.$format; + + if (file_exists($mappingPath)) { + throw new \RuntimeException(sprintf('Cannot generate entity when mapping "%s" already exists.', $mappingPath)); + } + + $mappingCode = $exporter->exportClassMetadata($class); + $entityGenerator->setGenerateAnnotations(false); + $entityCode = $entityGenerator->generateEntityClass($class); + } + + $this->filesystem->mkdir(dirname($entityPath)); + file_put_contents($entityPath, $entityCode); + + if ($mappingPath) { + $this->filesystem->mkdir(dirname($mappingPath)); + file_put_contents($mappingPath, $mappingCode); + } + + if ($withRepository) { + $path = $bundle->getPath().str_repeat('/..', substr_count(get_class($bundle), '\\')); + $this->getRepositoryGenerator()->writeEntityRepositoryClass($class->customRepositoryClassName, $path); + } + } + + public function isReservedKeyword($keyword) + { + return $this->registry->getConnection()->getDatabasePlatform()->getReservedKeywordsList()->isKeyword($keyword); + } + + protected function getEntityGenerator() + { + $entityGenerator = new EntityGenerator(); + $entityGenerator->setGenerateAnnotations(false); + $entityGenerator->setGenerateStubMethods(true); + $entityGenerator->setRegenerateEntityIfExists(false); + $entityGenerator->setUpdateEntityIfExists(true); + $entityGenerator->setNumSpaces(4); + $entityGenerator->setAnnotationPrefix('ORM\\'); + + return $entityGenerator; + } + + protected function getRepositoryGenerator() + { + return new EntityRepositoryGenerator(); + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineFormGenerator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineFormGenerator.php new file mode 100644 index 0000000..f083ebc --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/DoctrineFormGenerator.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Generator; + +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * Generates a form class based on a Doctrine entity. + * + * @author Fabien Potencier + * @author Hugo Hamon + */ +class DoctrineFormGenerator extends Generator +{ + private $filesystem; + private $skeletonDir; + private $className; + private $classPath; + + public function __construct(Filesystem $filesystem, $skeletonDir) + { + $this->filesystem = $filesystem; + $this->skeletonDir = $skeletonDir; + } + + public function getClassName() + { + return $this->className; + } + + public function getClassPath() + { + return $this->classPath; + } + + /** + * Generates the entity form class if it does not exist. + * + * @param BundleInterface $bundle The bundle in which to create the class + * @param string $entity The entity relative class name + * @param ClassMetadataInfo $metadata The entity metadata class + */ + public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata) + { + $parts = explode('\\', $entity); + $entityClass = array_pop($parts); + + $this->className = $entityClass.'Type'; + $dirPath = $bundle->getPath().'/Form'; + $this->classPath = $dirPath.'/'.str_replace('\\', '/', $entity).'Type.php'; + + if (file_exists($this->classPath)) { + throw new \RuntimeException(sprintf('Unable to generate the %s form class as it already exists under the %s file', $this->className, $this->classPath)); + } + + if (count($metadata->identifier) > 1) { + throw new \RuntimeException('The form generator does not support entity classes with multiple primary keys.'); + } + + $parts = explode('\\', $entity); + array_pop($parts); + + $this->renderFile($this->skeletonDir, 'FormType.php', $this->classPath, array( + 'dir' => $this->skeletonDir, + 'fields' => $this->getFieldsFromMetadata($metadata), + 'namespace' => $bundle->getNamespace(), + 'entity_namespace' => implode('\\', $parts), + 'entity_class' => $entityClass, + 'form_class' => $this->className, + 'form_type_name' => strtolower(str_replace('\\', '_', $bundle->getNamespace()).($parts ? '_' : '').implode('_', $parts).'_'.$this->className), + )); + } + + /** + * Returns an array of fields. Fields can be both column fields and + * association fields. + * + * @param ClassMetadataInfo $metadata + * @return array $fields + */ + private function getFieldsFromMetadata(ClassMetadataInfo $metadata) + { + $fields = (array) $metadata->fieldNames; + + // Remove the primary key field if it's not managed manually + if (!$metadata->isIdentifierNatural()) { + $fields = array_diff($fields, $metadata->identifier); + } + + foreach ($metadata->associationMappings as $fieldName => $relation) { + if ($relation['type'] !== ClassMetadataInfo::ONE_TO_MANY) { + $fields[] = $fieldName; + } + } + + return $fields; + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/Generator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/Generator.php new file mode 100644 index 0000000..a5d9f3f --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Generator/Generator.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Generator; + +/** + * Generator is the base class for all generators. + * + * @author Fabien Potencier + */ +class Generator +{ + protected function render($skeletonDir, $template, $parameters) + { + $twig = new \Twig_Environment(new \Twig_Loader_Filesystem($skeletonDir), array( + 'debug' => true, + 'cache' => false, + 'strict_variables' => true, + 'autoescape' => false, + )); + + return $twig->render($template, $parameters); + } + + protected function renderFile($skeletonDir, $template, $target, $parameters) + { + if (!is_dir(dirname($target))) { + mkdir(dirname($target), 0777, true); + } + + return file_put_contents($target, $this->render($skeletonDir, $template, $parameters)); + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/LICENSE b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/LICENSE new file mode 100644 index 0000000..c60ef6c --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Fabien Potencier + +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/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/KernelManipulator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/KernelManipulator.php new file mode 100644 index 0000000..8b1bbe7 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/KernelManipulator.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Manipulator; + +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * Changes the PHP code of a Kernel. + * + * @author Fabien Potencier + */ +class KernelManipulator extends Manipulator +{ + private $kernel; + private $reflected; + + /** + * Constructor. + * + * @param KernelInterface $kernel A KernelInterface instance + */ + public function __construct(KernelInterface $kernel) + { + $this->kernel = $kernel; + $this->reflected = new \ReflectionObject($kernel); + } + + /** + * Adds a bundle at the end of the existing ones. + * + * @param string $bundle The bundle class name + * + * @return Boolean true if it worked, false otherwise + * + * @throws \RuntimeException If bundle is already defined + */ + public function addBundle($bundle) + { + if (!$this->reflected->getFilename()) { + return false; + } + + $src = file($this->reflected->getFilename()); + $method = $this->reflected->getMethod('registerBundles'); + $lines = array_slice($src, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1); + + // Don't add same bundle twice + if (false !== strpos(implode('', $lines), $bundle)) { + throw new \RuntimeException(sprintf('Bundle "%s" is already defined in "AppKernel::registerBundles()".', $bundle)); + } + + $this->setCode(token_get_all('getStartLine()); + while ($token = $this->next()) { + // $bundles + if (T_VARIABLE !== $token[0] || '$bundles' !== $token[1]) { + continue; + } + + // = + $this->next(); + + // array + $token = $this->next(); + if (T_ARRAY !== $token[0]) { + return false; + } + + // add the bundle at the end of the array + while ($token = $this->next()) { + // look for ); + if (')' !== $this->value($token)) { + continue; + } + + if (';' !== $this->value($this->peek())) { + continue; + } + + // ; + $this->next(); + + $lines = array_merge( + array_slice($src, 0, $this->line - 2), + // Appends a separator comma to the current last position of the array + array(rtrim(rtrim($src[$this->line - 2]), ',') . ",\n"), + array(sprintf(" new %s(),\n", $bundle)), + array_slice($src, $this->line - 1) + ); + + file_put_contents($this->reflected->getFilename(), implode('', $lines)); + + return true; + } + } + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/Manipulator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/Manipulator.php new file mode 100644 index 0000000..a97baa7 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/Manipulator.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Manipulator; + + +/** + * Changes the PHP code of a Kernel. + * + * @author Fabien Potencier + */ +class Manipulator +{ + protected $tokens; + protected $line; + + /** + * Sets the code to manipulate. + * + * @param array $tokens An array of PHP tokens + * @param integer $line The start line of the code + */ + protected function setCode(array $tokens, $line = 0) + { + $this->tokens = $tokens; + $this->line = $line; + } + + /** + * Gets the next token. + * + * @param mixed A PHP token + */ + protected function next() + { + while ($token = array_shift($this->tokens)) { + $this->line += substr_count($this->value($token), "\n"); + + if (is_array($token) && in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) { + continue; + } + + return $token; + } + } + + /** + * Peeks the next token. + * + * @param mixed A PHP token + */ + protected function peek($nb = 1) + { + $i = 0; + $tokens = $this->tokens; + while ($token = array_shift($tokens)) { + if (is_array($token) && in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) { + continue; + } + + ++$i; + if ($i == $nb) { + return $token; + } + } + } + + /** + * Gets the value of a token. + * + * @param string The token value + */ + protected function value($token) + { + return is_array($token) ? $token[1] : $token; + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/RoutingManipulator.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/RoutingManipulator.php new file mode 100644 index 0000000..0026c47 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Manipulator/RoutingManipulator.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Manipulator; + +/** + * Changes the PHP code of a YAML routing file. + * + * @author Fabien Potencier + */ +class RoutingManipulator extends Manipulator +{ + private $file; + + /** + * Constructor. + * + * @param string $file The YAML routing file path + */ + public function __construct($file) + { + $this->file = $file; + } + + /** + * Adds a routing resource at the top of the existing ones. + * + * @param string $bundle + * @param string $format + * @param string $prefix + * @param string $path + * + * @return Boolean true if it worked, false otherwise + * + * @throws \RuntimeException If bundle is already imported + */ + public function addResource($bundle, $format, $prefix = '/', $path = 'routing') + { + $current = ''; + if (file_exists($this->file)) { + $current = file_get_contents($this->file); + + // Don't add same bundle twice + if (false !== strpos($current, $bundle)) { + throw new \RuntimeException(sprintf('Bundle "%s" is already imported.', $bundle)); + } + } elseif (!is_dir($dir = dirname($this->file))) { + mkdir($dir, 0777, true); + } + + $code = sprintf("%s:\n", $bundle.('/' !== $prefix ? '_'.str_replace('/', '_', substr($prefix, 1)) : '')); + if ('annotation' == $format) { + $code .= sprintf(" resource: \"@%s/Controller/\"\n type: annotation\n", $bundle); + } else { + $code .= sprintf(" resource: \"@%s/Resources/config/%s.%s\"\n", $bundle, $path, $format); + } + $code .= sprintf(" prefix: %s\n", $prefix); + $code .= "\n"; + $code .= $current; + + if (false === file_put_contents($this->file, $code)) { + return false; + } + + return true; + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/README.md b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/README.md new file mode 100644 index 0000000..8b033db --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/README.md @@ -0,0 +1,10 @@ +SensioGeneratorBundle +===================== + +The `SensioGeneratorBundle` extends the default Symfony2 command line +interface by providing new interactive and intuitive commands for generating +code skeletons like bundles, form classes, or CRUD controllers based on a +Doctrine 2 schema. + +More information in the official +[documentation](http://symfony.com/doc/current/bundles/SensioGeneratorBundle/index.html). diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/commands/generate_bundle.rst b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/commands/generate_bundle.rst new file mode 100644 index 0000000..90d04ba --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/commands/generate_bundle.rst @@ -0,0 +1,68 @@ +Generating a New Bundle Skeleton +================================ + +Usage +----- + +The ``generate:bundle`` generates a new bundle structure and automatically +activates it in the application. + +By default the command is run in the interactive mode and asks questions to +determine the bundle name, location, configuration format and default +structure: + +.. code-block:: bash + + php app/console generate:bundle + +To deactivate the interactive mode, use the `--no-interaction` option but don't +forget to pass all needed options: + +.. code-block:: bash + + php app/console generate:bundle --namespace=Acme/Bundle/BlogBundle --no-interaction + +Available Options +----------------- + +* ``--namespace``: The namespace of the bundle to create. The namespace should + begin with a "vendor" name like your company name, your project name, or + your client name, followed by one or more optional category sub-namespaces, + and it should end with the bundle name itself (which must have Bundle as a + suffix): + + .. code-block:: bash + + php app/console generate:bundle --namespace=Acme/Bundle/BlogBundle + +* ``--bundle-name``: The optional bundle name. It must be a string ending with + the ``Bundle`` suffix: + + .. code-block:: bash + + php app/console generate:bundle --bundle-name=AcmeBlogBundle + +* ``--dir``: The directory in which to store the bundle. By convention, the + command detects and uses the applications's ``src/`` folder: + + .. code-block:: bash + + php app/console generate:bundle --dir=/var/www/myproject/src + +* ``--format``: (**annotation**) [values: yml, xml, php or annotation] + Determine the format to use for the generated configuration files like + routing. By default, the command uses the ``annotation`` format. Choosing + the ``annotation`` format expects the ``SensioFrameworkExtraBundle`` is + already installed: + + .. code-block:: bash + + php app/console generate:bundle --format=annotation + +* ``--structure``: (**no**) [values: yes|no] Whether or not to generate a + complete default directory structure including empty public folders for + documentation, web assets and translations dictionaries: + + .. code-block:: bash + + php app/console generate:bundle --structure=yes diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/commands/generate_doctrine_crud.rst b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/commands/generate_doctrine_crud.rst new file mode 100644 index 0000000..07acc08 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/commands/generate_doctrine_crud.rst @@ -0,0 +1,65 @@ +Generating a CRUD Controller Based on a Doctrine Entity +======================================================= + +Usage +----- + +The ``generate:doctrine:crud`` generates a basic controller for a given entity +located in a given bundle. This controller allows to perform the five basic +operations on a model. + +* Listing all records, +* Showing one given record identified by its primary key, +* Creating a new record, +* Editing an existing record, +* Deleting an existing record. + +By default the command is run in the interactive mode and asks questions to +determine the entity name, the route prefix or whether or not to generate write +actions: + +.. code-block:: bash + + php app/console generate:doctrine:crud + +To deactivate the interactive mode, use the `--no-interaction` option but don't +forget to pass all needed options: + +.. code-block:: bash + + php app/console generate:doctrine:crud --entity=AcmeBlogBundle:Post --format=annotation --with-write --no-interaction + +Available Options +----------------- + +* ``--entity``: The entity name given as a shortcut notation containing the + bundle name in which the entity is located and the name of the entity. For + example: ``AcmeBlogBundle:Post``: + + .. code-block:: bash + + php app/console generate:doctrine:crud --entity=AcmeBlogBundle:Post + +* ``--route-prefix``: The prefix to use for each route that identifies an + action: + + .. code-block:: bash + + php app/console generate:doctrine:crud --route-prefix=acme_post + +* ``--with-write``: (**no**) [values: yes|no] Whether or not to generate the + `new`, `create`, `edit`, `update` and `delete` actions: + + .. code-block:: bash + + php app/console generate:doctrine:crud --with-write + +* ``--format``: (**annotation**) [values: yml, xml, php or annotation] + Determine the format to use for the generated configuration files like + routing. By default, the command uses the ``annotation`` format. Choosing + the ``annotation`` format expects the ``SensioFrameworkExtraBundle`` is + already installed: + + .. code-block:: bash + + php app/console generate:doctrine:crud --format=annotation diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/commands/generate_doctrine_entity.rst b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/commands/generate_doctrine_entity.rst new file mode 100644 index 0000000..7c362bd --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/commands/generate_doctrine_entity.rst @@ -0,0 +1,55 @@ +Generating a New Doctrine Entity Stub +===================================== + +Usage +----- + +The ``generate:doctrine:entity`` command generates a new Doctrine entity stub +including the mapping definition and the class properties, getters and setters. + +By default the command is run in the interactive mode and asks questions to +determine the bundle name, location, configuration format and default +structure: + +.. code-block:: bash + + php app/console generate:doctrine:entity + +The command can be run in a non interactive mode by using the +``--non-interaction`` option without forgetting all needed options: + +.. code-block:: bash + + php app/console generate:doctrine:entity --non-interaction --entity=AcmeBlogBundle:Post --fields="title:string(100) body:text" --format=xml + +Available Options +----------------- + +* ``--entity``: The entity name given as a shortcut notation containing the + bundle name in which the entity is located and the name of the entity. For + example: ``AcmeBlogBundle:Post``: + + .. code-block:: bash + + php app/console generate:doctrine:entity --entity=AcmeBlogBundle:Post + +* ``--fields``: The list of fields to generate in the entity class: + + .. code-block:: bash + + php app/console generate:doctrine:entity --fields="title:string(100) body:text" + +* ``--format``: (**annotation**) [values: yml, xml, php or annotation] This + option determines the format to use for the generated configuration files + like routing. By default, the command uses the ``annotation`` format: + + .. code-block:: bash + + php app/console generate:doctrine:entity --format=annotation + +* ``--with-repository``: This option tells whether or not to generate the + related Doctrine `EntityRepository` class: + + .. code-block:: bash + + php app/console generate:doctrine:entity --with-repository diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/commands/generate_doctrine_form.rst b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/commands/generate_doctrine_form.rst new file mode 100644 index 0000000..6bc9a60 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/commands/generate_doctrine_form.rst @@ -0,0 +1,23 @@ +Generating a New Form Type Class Based on a Doctrine Entity +=========================================================== + +Usage +----- + +The ``generate:doctrine:form`` generates a basic form type class by using the +metadata mapping of a given entity class: + +.. code-block:: bash + + php app/console generate:doctrine:form AcmeBlogBundle:Post + +Required Arguments +------------------ + +* ``entity``: The entity name given as a shortcut notation containing the + bundle name in which the entity is located and the name of the entity. For + example: ``AcmeBlogBundle:Post``: + + .. code-block:: bash + + php app/console generate:doctrine:form AcmeBlogBundle:Post diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/index.rst b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/index.rst new file mode 100644 index 0000000..a3ae3bc --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/doc/index.rst @@ -0,0 +1,42 @@ +SensioGeneratorBundle +========================== + +The ``SensioGeneratorBundle`` extends the default Symfony2 command line +interface by providing new interactive and intuitive commands for generating +code skeletons like bundles, form classes or CRUD controllers based on a +Doctrine 2 schema. + +Installation +------------ + +`Download`_ the bundle and put it under the ``Sensio\\Bundle\\`` namespace. +Then, like for any other bundle, include it in your Kernel class:: + + public function registerBundles() + { + $bundles = array( + ... + + new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(), + ); + + ... + } + +List of Available Commands +-------------------------- + +The ``SensioGeneratorBundle`` comes with four new commands that can be run in +interactive mode or not. The interactive mode asks you some questions to +configure the command parameters to generate the definitive code. The list of +new commands are listed below: + +.. toctree:: + :maxdepth: 1 + + commands/generate_bundle + commands/generate_doctrine_crud + commands/generate_doctrine_entity + commands/generate_doctrine_form + +.. _Download: http://github.com/sensio/SensioGeneratorBundle \ No newline at end of file diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/Bundle.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/Bundle.php new file mode 100644 index 0000000..7721df5 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/Bundle.php @@ -0,0 +1,9 @@ +root('{{ extension_alias }}'); + + // Here you should define the parameters that are allowed to + // configure your bundle. See the documentation linked above for + // more information on that topic. + + return $treeBuilder; + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/DefaultController.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/DefaultController.php new file mode 100644 index 0000000..2d98904 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/DefaultController.php @@ -0,0 +1,28 @@ +render('{{ bundle }}:Default:index.html.twig', array('name' => $name)); + {%- else -%} + return array('name' => $name); + {%- endif %} + + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/DefaultControllerTest.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/DefaultControllerTest.php new file mode 100644 index 0000000..9c76faa --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/DefaultControllerTest.php @@ -0,0 +1,17 @@ +request('GET', '/hello/Fabien'); + + $this->assertTrue($crawler->filter('html:contains("Hello Fabien")')->count() > 0); + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/Extension.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/Extension.php new file mode 100644 index 0000000..e493003 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/Extension.php @@ -0,0 +1,37 @@ +processConfiguration($configuration, $configs); + + {% if format == 'yml' -%} + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.yml'); + {%- elseif format == 'xml' or format == 'annotation' -%} + $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.xml'); + {%- elseif format == 'php' -%} + $loader = new Loader\PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.php'); + {%- endif %} + + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/index.html.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/index.html.twig new file mode 100644 index 0000000..e64fdb0 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/index.html.twig @@ -0,0 +1 @@ +Hello {% raw %}{{ name }}{% endraw %}! diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/messages.fr.xlf b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/messages.fr.xlf new file mode 100644 index 0000000..fd59e6c --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/messages.fr.xlf @@ -0,0 +1,11 @@ + + + + + + Symfony2 is great + J'aime Symfony2 + + + + diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.php new file mode 100644 index 0000000..67ae142 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.php @@ -0,0 +1,11 @@ +add('{{ bundle }}_homepage', new Route('/hello/{name}', array( + '_controller' => '{{ bundle }}:Default:index', +))); + +return $collection; diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.xml b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.xml new file mode 100644 index 0000000..b3164bf --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.xml @@ -0,0 +1,10 @@ + + + + + + {{ bundle }}:Default:index + + diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.yml b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.yml new file mode 100644 index 0000000..c418ad9 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/routing.yml @@ -0,0 +1,3 @@ +{{ bundle }}_homepage: + pattern: /hello/{name} + defaults: { _controller: {{ bundle }}:Default:index } diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.php new file mode 100644 index 0000000..71d6080 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.php @@ -0,0 +1,21 @@ +setDefinition( + '{{ extension_alias }}.example', + new Definition( + '{{ namespace }}\Example', + array( + new Reference('service_id'), + "plain_value", + new Parameter('parameter_name'), + ) + ) +); + +*/ \ No newline at end of file diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.xml b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.xml new file mode 100644 index 0000000..5f2c82f --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.yml b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.yml new file mode 100644 index 0000000..411a0f3 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/bundle/services.yml @@ -0,0 +1,7 @@ +parameters: +# {{ extension_alias }}.example.class: {{ namespace }}\Example + +services: +# {{ extension_alias }}.example: +# class: %{{ extension_alias }}.example.class% +# arguments: [@service_id, "plain_value", %parameter%] diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/create.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/create.php new file mode 100644 index 0000000..455e11f --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/create.php @@ -0,0 +1,42 @@ + + /** + * Creates a new {{ entity }} entity. + * +{% if 'annotation' == format %} + * @Route("/create", name="{{ route_name_prefix }}_create") + * @Method("post") + * @Template("{{ bundle }}:{{ entity }}:new.html.twig") +{% endif %} + */ + public function createAction() + { + $entity = new {{ entity_class }}(); + $request = $this->getRequest(); + $form = $this->createForm(new {{ entity_class }}Type(), $entity); + $form->bindRequest($request); + + if ($form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $em->persist($entity); + $em->flush(); + + {% if 'show' in actions -%} + return $this->redirect($this->generateUrl('{{ route_name_prefix }}_show', array('id' => $entity->getId()))); + {%- else -%} + return $this->redirect($this->generateUrl('{{ route_name_prefix }}')); + {%- endif %} + + } + +{% if 'annotation' == format %} + return array( + 'entity' => $entity, + 'form' => $form->createView(), + ); +{% else %} + return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:new.html.twig', array( + 'entity' => $entity, + 'form' => $form->createView(), + )); +{% endif %} + } diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/delete.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/delete.php new file mode 100644 index 0000000..1d01735 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/delete.php @@ -0,0 +1,38 @@ + + /** + * Deletes a {{ entity }} entity. + * +{% if 'annotation' == format %} + * @Route("/{id}/delete", name="{{ route_name_prefix }}_delete") + * @Method("post") +{% endif %} + */ + public function deleteAction($id) + { + $form = $this->createDeleteForm($id); + $request = $this->getRequest(); + + $form->bindRequest($request); + + if ($form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $entity = $em->getRepository('{{ bundle }}:{{ entity }}')->find($id); + + if (!$entity) { + throw $this->createNotFoundException('Unable to find {{ entity }} entity.'); + } + + $em->remove($entity); + $em->flush(); + } + + return $this->redirect($this->generateUrl('{{ route_name_prefix }}')); + } + + private function createDeleteForm($id) + { + return $this->createFormBuilder(array('id' => $id)) + ->add('id', 'hidden') + ->getForm() + ; + } \ No newline at end of file diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/edit.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/edit.php new file mode 100644 index 0000000..2371bed --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/edit.php @@ -0,0 +1,36 @@ + + /** + * Displays a form to edit an existing {{ entity }} entity. + * +{% if 'annotation' == format %} + * @Route("/{id}/edit", name="{{ route_name_prefix }}_edit") + * @Template() +{% endif %} + */ + public function editAction($id) + { + $em = $this->getDoctrine()->getManager(); + + $entity = $em->getRepository('{{ bundle }}:{{ entity }}')->find($id); + + if (!$entity) { + throw $this->createNotFoundException('Unable to find {{ entity }} entity.'); + } + + $editForm = $this->createForm(new {{ entity_class }}Type(), $entity); + $deleteForm = $this->createDeleteForm($id); + +{% if 'annotation' == format %} + return array( + 'entity' => $entity, + 'edit_form' => $editForm->createView(), + 'delete_form' => $deleteForm->createView(), + ); +{% else %} + return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:edit.html.twig', array( + 'entity' => $entity, + 'edit_form' => $editForm->createView(), + 'delete_form' => $deleteForm->createView(), + )); +{% endif %} + } diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/index.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/index.php new file mode 100644 index 0000000..6da9ec0 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/index.php @@ -0,0 +1,25 @@ + + /** + * Lists all {{ entity }} entities. + * +{% if 'annotation' == format %} + * @Route("/", name="{{ route_name_prefix }}") + * @Template() +{% endif %} + */ + public function indexAction() + { + $em = $this->getDoctrine()->getManager(); + + $entities = $em->getRepository('{{ bundle }}:{{ entity }}')->findAll(); + +{% if 'annotation' == format %} + return array( + 'entities' => $entities, + ); +{% else %} + return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:index.html.twig', array( + 'entities' => $entities, + )); +{% endif %} + } diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/new.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/new.php new file mode 100644 index 0000000..b79c311 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/new.php @@ -0,0 +1,26 @@ + + /** + * Displays a form to create a new {{ entity }} entity. + * +{% if 'annotation' == format %} + * @Route("/new", name="{{ route_name_prefix }}_new") + * @Template() +{% endif %} + */ + public function newAction() + { + $entity = new {{ entity_class }}(); + $form = $this->createForm(new {{ entity_class }}Type(), $entity); + +{% if 'annotation' == format %} + return array( + 'entity' => $entity, + 'form' => $form->createView(), + ); +{% else %} + return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:new.html.twig', array( + 'entity' => $entity, + 'form' => $form->createView(), + )); +{% endif %} + } diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/show.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/show.php new file mode 100644 index 0000000..abe5000 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/show.php @@ -0,0 +1,39 @@ + + /** + * Finds and displays a {{ entity }} entity. + * +{% if 'annotation' == format %} + * @Route("/{id}/show", name="{{ route_name_prefix }}_show") + * @Template() +{% endif %} + */ + public function showAction($id) + { + $em = $this->getDoctrine()->getManager(); + + $entity = $em->getRepository('{{ bundle }}:{{ entity }}')->find($id); + + if (!$entity) { + throw $this->createNotFoundException('Unable to find {{ entity }} entity.'); + } +{% if 'delete' in actions %} + + $deleteForm = $this->createDeleteForm($id); +{% endif %} + +{% if 'annotation' == format %} + return array( + 'entity' => $entity, +{% if 'delete' in actions %} + 'delete_form' => $deleteForm->createView(), +{% endif %} + ); +{% else %} + return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:show.html.twig', array( + 'entity' => $entity, +{% if 'delete' in actions %} + 'delete_form' => $deleteForm->createView(), +{%- endif %} + )); +{% endif %} + } diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/update.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/update.php new file mode 100644 index 0000000..1b2a6a2 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/actions/update.php @@ -0,0 +1,48 @@ + + /** + * Edits an existing {{ entity }} entity. + * +{% if 'annotation' == format %} + * @Route("/{id}/update", name="{{ route_name_prefix }}_update") + * @Method("post") + * @Template("{{ bundle }}:{{ entity }}:edit.html.twig") +{% endif %} + */ + public function updateAction($id) + { + $em = $this->getDoctrine()->getManager(); + + $entity = $em->getRepository('{{ bundle }}:{{ entity }}')->find($id); + + if (!$entity) { + throw $this->createNotFoundException('Unable to find {{ entity }} entity.'); + } + + $editForm = $this->createForm(new {{ entity_class }}Type(), $entity); + $deleteForm = $this->createDeleteForm($id); + + $request = $this->getRequest(); + + $editForm->bindRequest($request); + + if ($editForm->isValid()) { + $em->persist($entity); + $em->flush(); + + return $this->redirect($this->generateUrl('{{ route_name_prefix }}_edit', array('id' => $id))); + } + +{% if 'annotation' == format %} + return array( + 'entity' => $entity, + 'edit_form' => $editForm->createView(), + 'delete_form' => $deleteForm->createView(), + ); +{% else %} + return $this->render('{{ bundle }}:{{ entity|replace({'\\': '/'}) }}:edit.html.twig', array( + 'entity' => $entity, + 'edit_form' => $editForm->createView(), + 'delete_form' => $deleteForm->createView(), + )); +{% endif %} + } diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.php new file mode 100644 index 0000000..a47d00d --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.php @@ -0,0 +1,52 @@ +add('{{ route_name_prefix }}', new Route('/', array( + '_controller' => '{{ bundle }}:{{ entity }}:index', +))); +{% endif %} + +{% if 'show' in actions %} +$collection->add('{{ route_name_prefix }}_show', new Route('/{id}/show', array( + '_controller' => '{{ bundle }}:{{ entity }}:show', +))); +{% endif %} + +{% if 'new' in actions %} +$collection->add('{{ route_name_prefix }}_new', new Route('/new', array( + '_controller' => '{{ bundle }}:{{ entity }}:new', +))); + +$collection->add('{{ route_name_prefix }}_create', new Route( + '/create', + array('_controller' => '{{ bundle }}:{{ entity }}:create'), + array('_method' => 'post') +)); +{% endif %} + +{% if 'edit' in actions %} +$collection->add('{{ route_name_prefix }}_edit', new Route('/{id}/edit', array( + '_controller' => '{{ bundle }}:{{ entity }}:edit', +))); + +$collection->add('{{ route_name_prefix }}_update', new Route( + '/{id}/update', + array('_controller' => '{{ bundle }}:{{ entity }}:update'), + array('_method' => 'post') +)); +{% endif %} + +{% if 'delete' in actions %} +$collection->add('{{ route_name_prefix }}_delete', new Route( + '/{id}/delete', + array('_controller' => '{{ bundle }}:{{ entity }}:delete'), + array('_method' => 'post') +)); +{% endif %} + +return $collection; diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.xml b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.xml new file mode 100644 index 0000000..50fd85e --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.xml @@ -0,0 +1,44 @@ + + + + + + {{ bundle }}:{{ entity }}:index + + + + {{ bundle }}:{{ entity }}:show + + +{% if 'new' in actions %} + + {{ bundle }}:{{ entity }}:new + + + + {{ bundle }}:{{ entity }}:create + post + +{% endif %} + +{% if 'edit' in actions %} + + {{ bundle }}:{{ entity }}:edit + + + + {{ bundle }}:{{ entity }}:update + post + +{% endif %} + +{% if 'delete' in actions %} + + {{ bundle }}:{{ entity }}:delete + post + +{% endif %} + + diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.yml b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.yml new file mode 100644 index 0000000..a992678 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/config/routing.yml @@ -0,0 +1,40 @@ +{% if 'index' in actions %} +{{ route_name_prefix }}: + pattern: / + defaults: { _controller: "{{ bundle }}:{{ entity }}:index" } +{% endif %} + +{% if 'show' in actions %} +{{ route_name_prefix }}_show: + pattern: /{id}/show + defaults: { _controller: "{{ bundle }}:{{ entity }}:show" } +{% endif %} + +{% if 'new' in actions %} +{{ route_name_prefix }}_new: + pattern: /new + defaults: { _controller: "{{ bundle }}:{{ entity }}:new" } + +{{ route_name_prefix }}_create: + pattern: /create + defaults: { _controller: "{{ bundle }}:{{ entity }}:create" } + requirements: { _method: post } +{% endif %} + +{% if 'edit' in actions %} +{{ route_name_prefix }}_edit: + pattern: /{id}/edit + defaults: { _controller: "{{ bundle }}:{{ entity }}:edit" } + +{{ route_name_prefix }}_update: + pattern: /{id}/update + defaults: { _controller: "{{ bundle }}:{{ entity }}:update" } + requirements: { _method: post } +{% endif %} + +{% if 'delete' in actions %} +{{ route_name_prefix }}_delete: + pattern: /{id}/delete + defaults: { _controller: "{{ bundle }}:{{ entity }}:delete" } + requirements: { _method: post } +{% endif %} \ No newline at end of file diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/controller.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/controller.php new file mode 100644 index 0000000..e58856d --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/controller.php @@ -0,0 +1,49 @@ +request('GET', '/{{ route_prefix }}/'); + $this->assertTrue(200 === $client->getResponse()->getStatusCode()); + $crawler = $client->click($crawler->selectLink('Create a new entry')->link()); + + // Fill in the form and submit it + $form = $crawler->selectButton('Create')->form(array( + '{{ entity_class|lower }}[field_name]' => 'Test', + // ... other fields to fill + )); + + $client->submit($form); + $crawler = $client->followRedirect(); + + // Check data in the show view + $this->assertTrue($crawler->filter('td:contains("Test")')->count() > 0); + + // Edit the entity + $crawler = $client->click($crawler->selectLink('Edit')->link()); + + $form = $crawler->selectButton('Edit')->form(array( + '{{ entity_class|lower }}[field_name]' => 'Foo', + // ... other fields to fill + )); + + $client->submit($form); + $crawler = $client->followRedirect(); + + // Check the element contains an attribute with value equals "Foo" + $this->assertTrue($crawler->filter('[value="Foo"]')->count() > 0); + + // Delete the entity + $client->submit($crawler->selectButton('Delete')->form()); + $crawler = $client->followRedirect(); + + // Check the entity has been delete on the list + $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); + } \ No newline at end of file diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/tests/others/short_scenario.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/tests/others/short_scenario.php new file mode 100644 index 0000000..09866f0 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/tests/others/short_scenario.php @@ -0,0 +1,14 @@ + + public function testCompleteScenario() + { + // Create a new client to browse the application + $client = static::createClient(); + + // Go to the list view + $crawler = $client->request('GET', '/{{ route_prefix }}/'); + $this->assertTrue(200 === $client->getResponse()->getStatusCode()); + + // Go to the show view + $crawler = $client->click($crawler->selectLink('show')->link()); + $this->assertTrue(200 === $client->getResponse()->getStatusCode()); + } \ No newline at end of file diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/tests/test.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/tests/test.php new file mode 100644 index 0000000..afb382f --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/tests/test.php @@ -0,0 +1,18 @@ +{{ entity }} edit + +
    + {{ "{{ form_widget(edit_form) }}" }} +

    + +

    + + +{% set hide_edit, hide_delete = true, false %} +{% include 'views/others/record_actions.html.twig' %} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/index.html.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/index.html.twig new file mode 100644 index 0000000..882984e --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/index.html.twig @@ -0,0 +1,56 @@ +

    {{ entity }} list

    + +
    + + + {%- for field, metadata in fields %} + + + + {%- endfor %} + + + + + + {{ '{% for entity in entities %}' }} + + + {%- for field, metadata in fields %} + {%- if loop.first and ('show' in actions) %} + + + + {%- elseif metadata.type in ['date', 'datetime'] %} + + + + {%- else %} + + + + {%- endif %} + + {%- if loop.last %} + + + + {%- endif %} + {%- endfor %} + + + {{ '{% endfor %}' }} + +
    {{ field|capitalize }}Actions
    {{ '{{ entity.'~ field|replace({'_': ''}) ~' }}' }}{{ '{% if entity.'~ field|replace({'_': ''}) ~' %}{{ entity.'~ field|replace({'_': ''}) ~'|date(\'Y-m-d H:i:s\') }}{% endif %}' }}{{ '{{ entity.'~ field|replace({'_': ''}) ~' }}' }} + {%- include "views/others/actions.html.twig" %} +
    + +{% if 'new' in actions %} + +{% endif %} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/new.html.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/new.html.twig new file mode 100644 index 0000000..06422f6 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/new.html.twig @@ -0,0 +1,11 @@ +

    {{ entity }} creation

    + +
    + {{ "{{ form_widget(form) }}" }} +

    + +

    +
    + +{% set hide_edit, hide_delete = true, true %} +{% include 'views/others/record_actions.html.twig' %} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/others/actions.html.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/others/actions.html.twig new file mode 100644 index 0000000..5137ff2 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/others/actions.html.twig @@ -0,0 +1,12 @@ + +
      + + {%- for action in record_actions %} + +
    • + {{ action }} +
    • + + {%- endfor %} + +
    diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/others/record_actions.html.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/others/record_actions.html.twig new file mode 100644 index 0000000..13c2673 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/others/record_actions.html.twig @@ -0,0 +1,22 @@ +
      +
    • + + Back to the list + +
    • +{% if ('edit' in actions) and (not hide_edit) %} +
    • + + Edit + +
    • +{% endif %} +{% if ('delete' in actions) and (not hide_delete) %} +
    • +
      + {{ '{{ form_widget(delete_form) }}' }} + +
      +
    • +{% endif %} +
    diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/show.html.twig b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/show.html.twig new file mode 100644 index 0000000..2badbd0 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/crud/views/show.html.twig @@ -0,0 +1,28 @@ +

    {{ entity }}

    + + + + {%- for field, metadata in fields %} + + + + + {%- if metadata.type in ['date', 'datetime'] %} + + + + {%- else %} + + + + {%- endif %} + + + + {%- endfor %} + + +
    {{ field|capitalize }}{{ '{{ entity.'~ field|replace({'_': ''}) ~'|date(\'Y-m-d H:i:s\') }}' }}{{ '{{ entity.'~ field|replace({'_': ''}) ~' }}' }}
    + +{% set hide_edit, hide_delete = false, false %} +{% include 'views/others/record_actions.html.twig' %} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/form/FormType.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/form/FormType.php new file mode 100644 index 0000000..60154ee --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/Resources/skeleton/form/FormType.php @@ -0,0 +1,34 @@ +add('{{ field }}') + + {%- endfor %} + + ; + } + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'data_class' => '{{ namespace }}\Entity{{ entity_namespace ? '\\' ~ entity_namespace : '' }}\{{ entity_class }}' + )); + } + + public function getName() + { + return '{{ form_type_name }}'; + } +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/SensioGeneratorBundle.php b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/SensioGeneratorBundle.php new file mode 100644 index 0000000..5c4e799 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/SensioGeneratorBundle.php @@ -0,0 +1,23 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Sensio\Bundle\GeneratorBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * SensioGeneratorBundle. + * + * @author Fabien Potencier + */ +class SensioGeneratorBundle extends Bundle +{ +} diff --git a/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/composer.json b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/composer.json new file mode 100644 index 0000000..9195908 --- /dev/null +++ b/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/composer.json @@ -0,0 +1,30 @@ +{ + "name": "sensio/generator-bundle", + "description": "This bundle generates code for you", + "keywords": [], + "type": "symfony-bundle", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "symfony/framework-bundle": "2.1.*" + }, + "require-dev": { + "symfony/doctrine-bridge": "2.1.*", + "doctrine/orm": ">=2.1,<2.4-dev", + "twig/twig": ">=1.8,<2.0-dev" + }, + "autoload": { + "psr-0": { "Sensio\\Bundle\\GeneratorBundle": "" } + }, + "target-dir": "Sensio/Bundle/GeneratorBundle", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/LICENSE b/vendor/swiftmailer/swiftmailer/LICENSE new file mode 100644 index 0000000..fc8a5de --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/vendor/swiftmailer/swiftmailer/VERSION b/vendor/swiftmailer/swiftmailer/VERSION new file mode 100644 index 0000000..16dd027 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/VERSION @@ -0,0 +1 @@ +Swift-4.1.9 diff --git a/vendor/swiftmailer/swiftmailer/composer.json b/vendor/swiftmailer/swiftmailer/composer.json new file mode 100644 index 0000000..1ed07af --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/composer.json @@ -0,0 +1,29 @@ +{ + "name": "swiftmailer/swiftmailer", + "type": "library", + "description": "Swiftmailer, free feature-rich PHP mailer", + "keywords": ["mail","mailer"], + "homepage": "http://swiftmailer.org", + "license": "LGPL", + "authors": [ + { + "name": "Chris Corbyn", + "email": "" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "php": ">=5.2.4" + }, + "autoload": { + "files": ["lib/swift_required.php"] + }, + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/doc/headers.rst b/vendor/swiftmailer/swiftmailer/doc/headers.rst new file mode 100644 index 0000000..18796ec --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/headers.rst @@ -0,0 +1,742 @@ +Message Headers +=============== + +Sometimes you'll want to add your own headers to a message or modify/remove +headers that are already present. You work with the message's HeaderSet to do +this. + +Header Basics +------------- + +All MIME entities in Swift Mailer -- including the message itself -- +store their headers in a single object called a HeaderSet. This HeaderSet is +retrieved with the ``getHeaders()`` method. + +As mentioned in the previous chapter, everything that forms a part of a message +in Swift Mailer is a MIME entity that is represented by an instance of +``Swift_Mime_MimeEntity``. This includes -- most notably -- the message object +itself, attachments, MIME parts and embedded images. Each of these MIME entities +consists of a body and a set of headers that describe the body. + +For all of the "standard" headers in these MIME entities, such as the +``Content-Type``, there are named methods for working with them, such as +``setContentType()`` and ``getContentType()``. This is because headers are a +moderately complex area of the library. Each header has a slightly different +required structure that it must meet in order to comply with the standards that +govern email (and that are checked by spam blockers etc). + +You fetch the HeaderSet from a MIME entity like so: + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + // Fetch the HeaderSet from a Message object + $headers = $message->getHeaders(); + + $attachment = Swift_Attachment::fromPath('document.pdf'); + + // Fetch the HeaderSet from an attachment object + $headers = $attachment->getHeaders(); + +The job of the HeaderSet is to contain and manage instances of Header objects. +Depending upon the MIME entity the HeaderSet came from, the contents of the +HeaderSet will be different, since an attachment for example has a different +set of headers to those in a message. + +You can find out what the HeaderSet contains with a quick loop, dumping out +the names of the headers: + +.. code-block:: php + + foreach ($headers->getAll() as $header) { + printf("%s
    \n", $header->getFieldName()); + } + + /* + Content-Transfer-Encoding + Content-Type + MIME-Version + Date + Message-ID + From + Subject + To + */ + +You can also dump out the rendered HeaderSet by calling its ``toString()`` +method: + +.. code-block:: php + + echo $headers->toString(); + + /* + Message-ID: <1234869991.499a9ee7f1d5e@swift.generated> + Date: Tue, 17 Feb 2009 22:26:31 +1100 + Subject: Awesome subject! + From: sender@example.org + To: recipient@example.org + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: quoted-printable + */ + +Where the complexity comes in is when you want to modify an existing header. +This complexity comes from the fact that each header can be of a slightly +different type (such as a Date header, or a header that contains email +addresses, or a header that has key-value parameters on it!). Each header in the +HeaderSet is an instance of ``Swift_Mime_Header``. They all have common +functionality, but knowing exactly what type of header you're working with will +allow you a little more control. + +You can determine the type of header by comparing the return value of its +``getFieldType()`` method with the constants ``TYPE_TEXT``, +``TYPE_PARAMETERIZED``, ``TYPE_DATE``, ``TYPE_MAILBOX``, ``TYPE_ID`` and +``TYPE_PATH`` which are defined in ``Swift_Mime_Header``. + + +.. code-block:: php + + foreach ($headers->getAll() as $header) { + switch ($header->getFieldType()) { + case Swift_Mime_Header::TYPE_TEXT: $type = 'text'; + break; + case Swift_Mime_Header::TYPE_PARAMETERIZED: $type = 'parameterized'; + break; + case Swift_Mime_Header::TYPE_MAILBOX: $type = 'mailbox'; + break; + case Swift_Mime_Header::TYPE_DATE: $type = 'date'; + break; + case Swift_Mime_Header::TYPE_ID: $type = 'ID'; + break; + case Swift_Mime_Header::TYPE_PATH: $type = 'path'; + break; + } + printf("%s: is a %s header
    \n", $header->getFieldName(), $type); + } + + /* + Content-Transfer-Encoding: is a text header + Content-Type: is a parameterized header + MIME-Version: is a text header + Date: is a date header + Message-ID: is a ID header + From: is a mailbox header + Subject: is a text header + To: is a mailbox header + */ + +Headers can be removed from the set, modified within the set, or added to the +set. + +The following sections show you how to work with the HeaderSet and explain the +details of each implementation of ``Swift_Mime_Header`` that may +exist within the HeaderSet. + +Header Types +------------ + +Because all headers are modeled on different data (dates, addresses, text!) +there are different types of Header in Swift Mailer. Swift Mailer attempts to +categorize all possible MIME headers into more general groups, defined by a +small number of classes. + +Text Headers +~~~~~~~~~~~~ + +Text headers are the simplest type of Header. They contain textual information +with no special information included within it -- for example the Subject +header in a message. + +There's nothing particularly interesting about a text header, though it is +probably the one you'd opt to use if you need to add a custom header to a +message. It represents text just like you'd think it does. If the text +contains characters that are not permitted in a message header (such as new +lines, or non-ascii characters) then the header takes care of encoding the +text so that it can be used. + +No header -- including text headers -- in Swift Mailer is vulnerable to +header-injection attacks. Swift Mailer breaks any attempt at header injection by +encoding the dangerous data into a non-dangerous form. + +It's easy to add a new text header to a HeaderSet. You do this by calling the +HeaderSet's ``addTextHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addTextHeader('Your-Header-Name', 'the header value'); + +Changing the value of an existing text header is done by calling it's +``setValue()`` method. + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('new subject'); + +When output via ``toString()``, a text header produces something like the +following: + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('amazing subject line'); + + echo $subject->toString(); + + /* + + Subject: amazing subject line + + */ + +If the header contains any characters that are outside of the US-ASCII range +however, they will be encoded. This is nothing to be concerned about since +mail clients will decode them back. + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('contains – dash'); + + echo $subject->toString(); + + /* + + Subject: contains =?utf-8?Q?=E2=80=93?= dash + + */ + +Parameterized Headers +~~~~~~~~~~~~~~~~~~~~~ + +Parameterized headers are text headers that contain key-value parameters +following the textual content. The Content-Type header of a message is a +parameterized header since it contains charset information after the content +type. + +The parameterized header type is a special type of text header. It extends the +text header by allowing additional information to follow it. All of the methods +from text headers are available in addition to the methods described here. + +Adding a parameterized header to a HeaderSet is done by using the +``addParameterizedHeader()`` method which takes a text value like +``addTextHeader()`` but it also accepts an associative array of +key-value parameters. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addParameterizedHeader( + 'Header-Name', 'header value', + array('foo' => 'bar') + ); + +To change the text value of the header, call it's ``setValue()`` method just as +you do with text headers. + +To change the parameters in the header, call the header's ``setParameters()`` +method or the ``setParameter()`` method (note the pluralization). + +.. code-block:: php + + $type = $message->getHeaders()->get('Content-Type'); + + // setParameters() takes an associative array + $type->setParameters(array( + 'name' => 'file.txt', + 'charset' => 'iso-8859-1' + )); + + // setParameter() takes two args for $key and $value + $type->setParameter('charset', 'iso-8859-1'); + +When output via ``toString()``, a parameterized header produces something like +the following: + +.. code-block:: php + + $type = $message->getHeaders()->get('Content-Type'); + + $type->setValue('text/html'); + $type->setParameter('charset', 'utf-8'); + + echo $type->toString(); + + /* + + Content-Type: text/html; charset=utf-8 + + */ + +If the header contains any characters that are outside of the US-ASCII range +however, they will be encoded, just like they are for text headers. This is +nothing to be concerned about since mail clients will decode them back. +Likewise, if the parameters contain any non-ascii characters they will be +encoded so that they can be transmitted safely. + +.. code-block:: php + + $attachment = Swift_Attachment::newInstance(); + + $disp = $attachment->getHeaders()->get('Content-Disposition'); + + $disp->setValue('attachment'); + $disp->setParameter('filename', 'report–may.pdf'); + + echo $disp->toString(); + + /* + + Content-Disposition: attachment; filename*=utf-8''report%E2%80%93may.pdf + + */ + +Date Headers +~~~~~~~~~~~~ + +Date headers contains an RFC 2822 formatted date (i.e. what PHP's ``date('r')`` +returns). They are used anywhere a date or time is needed to be presented as a +message header. + +The data on which a date header is modeled is simply a UNIX timestamp such as +that returned by ``time()`` or ``strtotime()``. The timestamp is used to create +a correctly structured RFC 2822 formatted date such as +``Tue, 17 Feb 2009 22:26:31 +1100``. + +The obvious place this header type is used is in the ``Date:`` header of the +message itself. + +It's easy to add a new date header to a HeaderSet. You do this by calling +the HeaderSet's ``addDateHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addDateHeader('Your-Header-Name', strtotime('3 days ago')); + +Changing the value of an existing date header is done by calling it's +``setTimestamp()`` method. + +.. code-block:: php + + $date = $message->getHeaders()->get('Date'); + + $date->setTimestamp(time()); + +When output via ``toString()``, a date header produces something like the +following: + +.. code-block:: php + + $date = $message->getHeaders()->get('Date'); + + echo $date->toString(); + + /* + + Date: Wed, 18 Feb 2009 13:35:02 +1100 + + */ + +Mailbox (e-mail address) Headers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Mailbox headers contain one or more email addresses, possibly with +personalized names attached to them. The data on which they are modeled is +represented by an associative array of email addresses and names. + +Mailbox headers are probably the most complex header type to understand in +Swift Mailer because they accept their input as an array which can take various +forms, as described in the previous chapter. + +All of the headers that contain e-mail addresses in a message -- with the +exception of ``Return-Path:`` which has a stricter syntax -- use this header +type. That is, ``To:``, ``From:`` etc. + +You add a new mailbox header to a HeaderSet by calling the HeaderSet's +``addMailboxHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addMailboxHeader('Your-Header-Name', array( + 'person1@example.org' => 'Person Name One', + 'person2@example.org', + 'person3@example.org', + 'person4@example.org' => 'Another named person' + )); + +Changing the value of an existing mailbox header is done by calling it's +``setNameAddresses()`` method. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setNameAddresses(array( + 'joe@example.org' => 'Joe Bloggs', + 'john@example.org' => 'John Doe', + 'no-name@example.org' + )); + +If you don't wish to concern yourself with the complicated accepted input +formats accepted by ``setNameAddresses()`` as described in the previous chapter +and you only want to set one or more addresses (not names) then you can just +use the ``setAddresses()`` method instead. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setAddresses(array( + 'joe@example.org', + 'john@example.org', + 'no-name@example.org' + )); + +.. note:: + + Both methods will accept the above input format in practice. + +If all you want to do is set a single address in the header, you can use a +string as the input parameter to ``setAddresses()`` and/or +``setNameAddresses()``. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setAddresses('joe-bloggs@example.org'); + +When output via ``toString()``, a mailbox header produces something like the +following: + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setNameAddresses(array( + 'person1@example.org' => 'Name of Person', + 'person2@example.org', + 'person3@example.org' => 'Another Person' + )); + + echo $to->toString(); + + /* + + To: Name of Person , person2@example.org, Another Person + + + */ + +ID Headers +~~~~~~~~~~ + +ID headers contain identifiers for the entity (or the message). The most +notable ID header is the Message-ID header on the message itself. + +An ID that exists inside an ID header looks more-or-less less like an email +address. For example, ``]]>``. +The part to the left of the @ sign is usually unique, based on the current time +and some random factor. The part on the right is usually a domain name. + +Any ID passed the an ID header's ``setId()`` method absolutely MUST conform to +this structure, otherwise you'll get an Exception thrown at you by Swift Mailer +(a ``Swift_RfcComplianceException``). This is to ensure that the generated +email complies with relevant RFC documents and therefore is less likely to be +blocked as spam. + +It's easy to add a new ID header to a HeaderSet. You do this by calling +the HeaderSet's ``addIdHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addIdHeader('Your-Header-Name', '123456.unqiue@example.org'); + +Changing the value of an existing date header is done by calling its +``setId()`` method. + +.. code-block:: php + + $msgId = $message->getHeaders()->get('Message-ID'); + + $msgId->setId(time() . '.' . uniqid('thing') . '@example.org'); + +When output via ``toString()``, an ID header produces something like the +following: + +.. code-block:: php + + $msgId = $message->getHeaders()->get('Message-ID'); + + echo $msgId->toString(); + + /* + + Message-ID: <1234955437.499becad62ec2@example.org> + + */ + +Path Headers +~~~~~~~~~~~~ + +Path headers are like very-restricted mailbox headers. They contain a single +email address with no associated name. The Return-Path header of a message is +a path header. + +You add a new path header to a HeaderSet by calling the HeaderSet's +``addPathHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addPathHeader('Your-Header-Name', 'person@example.org'); + + +Changing the value of an existing path header is done by calling its +``setAddress()`` method. + +.. code-block:: php + + $return = $message->getHeaders()->get('Return-Path'); + + $return->setAddress('my-address@example.org'); + +When output via ``toString()``, a path header produces something like the +following: + +.. code-block:: php + + $return = $message->getHeaders()->get('Return-Path'); + + $return->setAddress('person@example.org'); + + echo $return->toString(); + + /* + + Return-Path: + + */ + +Header Operations +----------------- + +Working with the headers in a message involves knowing how to use the methods +on the HeaderSet and on the individual Headers within the HeaderSet. + +Adding new Headers +~~~~~~~~~~~~~~~~~~ + +New headers can be added to the HeaderSet by using one of the provided +``add..Header()`` methods. + +To add a header to a MIME entity (such as the message): + +Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Add the header to the HeaderSet by calling one of the ``add..Header()`` + methods. + +The added header will appear in the message when it is sent. + +.. code-block:: php + + // Adding a custom header to a message + $message = Swift_Message::newInstance(); + $headers = $message->getHeaders(); + $headers->addTextHeader('X-Mine', 'something here'); + + // Adding a custom header to an attachment + $attachment = Swift_Attachment::fromPath('/path/to/doc.pdf'); + $attachment->getHeaders()->addDateHeader('X-Created-Time', time()); + +Retrieving Headers +~~~~~~~~~~~~~~~~~~ + +Headers are retrieved through the HeaderSet's ``get()`` and ``getAll()`` +methods. + +To get a header, or several headers from a MIME entity: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Get the header(s) from the HeaderSet by calling either ``get()`` or + ``getAll()``. + +When using ``get()`` a single header is returned that matches the name (case +insensitive) that is passed to it. When using ``getAll()`` with a header name, +an array of headers with that name are returned. Calling ``getAll()`` with no +arguments returns an array of all headers present in the entity. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``getAll()`` exists to fetch all + headers with a specified name. In addition, ``get()`` accepts an optional + numerical index, starting from zero to specify which header you want more + specifically. + +.. note:: + + If you want to modify the contents of the header and you don't know for + sure what type of header it is then you may need to check the type by + calling its ``getFieldType()`` method. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Get the To: header + $toHeader = $headers->get('To'); + + // Get all headers named "X-Foo" + $fooHeaders = $headers->getAll('X-Foo'); + + // Get the second header named "X-Foo" + $foo = $headers->get('X-Foo', 1); + + // Get all headers that are present + $all = $headers->getAll(); + +Check if a Header Exists +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can check if a named header is present in a HeaderSet by calling its +``has()`` method. + +To check if a header exists: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Call the HeaderSet's ``has()`` method specifying the header you're looking + for. + +If the header exists, ``true`` will be returned or ``false`` if not. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``has()`` accepts an optional + numerical index, starting from zero to specify which header you want to + check more specifically. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Check if the To: header exists + if ($headers->has('To')) { + echo 'To: exists'; + } + + // Check if an X-Foo header exists twice (i.e. check for the 2nd one) + if ($headers->has('X-Foo', 1)) { + echo 'Second X-Foo header exists'; + } + +Removing Headers +~~~~~~~~~~~~~~~~ + +Removing a Header from the HeaderSet is done by calling the HeaderSet's +``remove()`` or ``removeAll()`` methods. + +To remove an existing header: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Call the HeaderSet's ``remove()`` or ``removeAll()`` methods specifying the + header you want to remove. + +When calling ``remove()`` a single header will be removed. When calling +``removeAll()`` all headers with the given name will be removed. If no headers +exist with the given name, no errors will occur. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``remove()`` accepts an optional + numerical index, starting from zero to specify which header you want to + check more specifically. For the same reason, ``removeAll()`` exists to + remove all headers that have the given name. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Remove the Subject: header + $headers->remove('Subject'); + + // Remove all X-Foo headers + $headers->removeAll('X-Foo'); + + // Remove only the second X-Foo header + $headers->remove('X-Foo', 1); + +Modifying a Header's Content +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To change a Header's content you should know what type of header it is and then +call it's appropriate setter method. All headers also have a +``setFieldBodyModel()`` method that accepts a mixed parameter and delegates to +the correct setter. + +To modify an existing header: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Get the Header by using the HeaderSet's ``get()``. + +* Call the Header's appropriate setter method or call the header's + ``setFieldBodyModel()`` method. + +The header will be updated inside the HeaderSet and the changes will be seen +when the message is sent. + +.. code-block:: php + + $headers = $message->getHeaders(); + + // Change the Subject: header + $subj = $headers->get('Subject'); + $subj->setValue('new subject here'); + + // Change the To: header + $to = $headers->get('To'); + $to->setNameAddresses(array( + 'person@example.org' => 'Person', + 'thing@example.org' + )); + + // Using the setFieldBodyModel() just delegates to the correct method + // So here to calls setNameAddresses() + $to->setFieldBodyModel(array( + 'person@example.org' => 'Person', + 'thing@example.org' + )); diff --git a/vendor/swiftmailer/swiftmailer/doc/help-resources.rst b/vendor/swiftmailer/swiftmailer/doc/help-resources.rst new file mode 100644 index 0000000..9820653 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/help-resources.rst @@ -0,0 +1,44 @@ +Getting Help +============ + +There are a number of ways you can get help when using Swift Mailer, depending +upon the nature of your problem. For bug reports and feature requests create a +new ticket in Github. For general advice ask on the Google Group +(swiftmailer). + +Submitting Bugs & Feature Requests +---------------------------------- + +Bugs and feature requests should be posted on Github. + +If you post a bug or request a feature in the forum, or on the Google Group +you will most likely be asked to create a ticket in `Github`_ since it is +the simply not feasible to manage such requests from a number of a different +sources. + +When you go to Github you will be asked to create a username and password +before you can create a ticket. This is free and takes very little time. + +When you create your ticket, do not assign it to any milestones. A developer +will assess your ticket and re-assign it as needed. + +If your ticket is reporting a bug present in the current version, which was +not present in the previous version please include the tag "regression" in +your ticket. + +Github will update you when work is performed on your ticket. + +Ask on the Google Group +----------------------- + +You can seek advice at Google Groups, within the "swiftmailer" `group`_. + +You can post messages to this group if you want help, or there's something you +wish to discuss with the developers and with other users. + +This is probably the fastest way to get help since it is primarily email-based +for most users, though bug reports should not be posted here since they may +not be resolved. + +.. _`Github`: https://github.com/swiftmailer/swiftmailer/issues +.. _`group`: http://groups.google.com/group/swiftmailer diff --git a/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst b/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst new file mode 100644 index 0000000..34d0a4f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst @@ -0,0 +1,30 @@ +Including Swift Mailer (Autoloading) +==================================== + +Swift Mailer uses an autoloader so the only file you need to include is the +``lib/swift_required.php`` file. + +To use Swift Mailer's autoloader: + +* Put Swift Mailer somewhere accessible to your PHP scripts (this does not + need to be in the web root). + +* Include, or require the ``lib/swift_required.php`` file. + +* Follow the remainder of the documentation for using the available + components. + +.. note:: + + While Swift Mailer's autoloader is designed to play nicely with other + autoloaders, sometimes you may have a need to avoid using Swift Mailer's + autoloader and use your own instead. Include the ``swift_init.php`` + instead of the ``swift_required.php`` if you need to do this. The very + minimum include is the ``swift_init.php`` file since Swift Mailer will not + work without the dependency injection this file sets up: + + .. code-block:: php + + require_once '/path/to/swift-mailer/lib/swift_required.php'; + + /* rest of code goes here */ diff --git a/vendor/swiftmailer/swiftmailer/doc/index.rst b/vendor/swiftmailer/swiftmailer/doc/index.rst new file mode 100644 index 0000000..7c75658 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/index.rst @@ -0,0 +1,15 @@ +Swiftmailer +=========== + +.. toctree:: + :maxdepth: 2 + + introduction + overview + installing + help-resources + including-the-files + messages + headers + sending + plugins diff --git a/vendor/swiftmailer/swiftmailer/doc/installing.rst b/vendor/swiftmailer/swiftmailer/doc/installing.rst new file mode 100644 index 0000000..31d8fe5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/installing.rst @@ -0,0 +1,201 @@ +Installing the Library +====================== + +Installing Swift Mailer is trivial. Usually it's just a case of uploading the +extracted source files to your web server. + +Installing from PEAR +-------------------- + +If you want to install Swift Mailer globally on your machine, the easiest +installation method is using the PEAR channel. + +To install the Swift Mailer PEAR package: + +* Run the command ``pear channel-discover pear.swiftmailer.org``. + +* Then, run the command ``pear install swift/swift``. + +Installing from a Package +------------------------- + +Most users will download a package from the Swift Mailer website and install +Swift Mailer using this. + +If you downloaded Swift Mailer as a ``.tar.gz`` or +``.zip`` file installation is as simple as extracting the archive +and uploading it to your web server. + +Extracting the Library +~~~~~~~~~~~~~~~~~~~~~~ + +You extract the archive by using your favorite unarchiving tool such as +``tar`` or 7-Zip. + +You will need to have access to a program that can open uncompress the +archive. On Windows computers, 7-Zip will work. On Mac and Linux systems you +can use ``tar`` on the command line. + +To extract your downloaded package: + +* Use the "extract" facility of your archiving software. + +The source code will be placed into a directory with the same name as the +archive (e.g. Swift-4.0.0-b1). + +The following example shows the process on Mac OS X and Linux systems using +the ``tar`` command. + +.. code-block:: bash + + $ ls + Swift-4.0.0-dev.tar.gz + $ tar xvzf Swift-4.0.0-dev.tar.gz + Swift-4.0.0-dev/ + Swift-4.0.0-dev/lib/ + Swift-4.0.0-dev/lib/classes/ + Swift-4.0.0-dev/lib/classes/Swift/ + Swift-4.0.0-dev/lib/classes/Swift/ByteStream/ + Swift-4.0.0-dev/lib/classes/Swift/CharacterReader/ + Swift-4.0.0-dev/lib/classes/Swift/CharacterReaderFactory/ + Swift-4.0.0-dev/lib/classes/Swift/CharacterStream/ + Swift-4.0.0-dev/lib/classes/Swift/Encoder/ + + ... etc etc ... + + Swift-4.0.0-dev/tests/unit/Swift/Transport/LoadBalancedTransportTest.php + Swift-4.0.0-dev/tests/unit/Swift/Transport/SendmailTransportTest.php + Swift-4.0.0-dev/tests/unit/Swift/Transport/StreamBufferTest.php + $ cd Swift-4.0.0-dev + $ ls + CHANGES LICENSE.GPL LICENSE.LGPL README VERSION examples lib test-suite tests + $ + +Installing from Git +------------------- + +It's possible to download and install Swift Mailer directly from github.com if +you want to keep up-to-date with ease. + +Swift Mailer's source code is kept in a git repository at github.com so you +can get the source directly from the repository. + +.. note:: + + You do not need to have git installed to use Swift Mailer from github. If + you don't have git installed, go to `github`_ and click the "Download" + button. + +Cloning the Repository +~~~~~~~~~~~~~~~~~~~~~~ + +The repository can be cloned from git://github.com/swiftmailer/swiftmailer.git +using the ``git clone`` command. + +You will need to have ``git`` installed before you can use the +``git clone`` command. + +To clone the repository: + +* Open your favorite terminal environment (command line). + +* Move to the directory you want to clone to. + +* Run the command ``git clone git://github.com/swiftmailer/swiftmailer.git + swiftmailer``. + +The source code will be downloaded into a directory called "swiftmailer". + +The example shows the process on a UNIX-like system such as Linux, BSD or Mac +OS X. + +.. code-block:: bash + + $ cd source_code/ + $ git clone git://github.com/swiftmailer/swiftmailer.git swiftmailer + Initialized empty Git repository in /Users/chris/source_code/swiftmailer/.git/ + remote: Counting objects: 6815, done. + remote: Compressing objects: 100% (2761/2761), done. + remote: Total 6815 (delta 3641), reused 6326 (delta 3286) + Receiving objects: 100% (6815/6815), 4.35 MiB | 162 KiB/s, done. + Resolving deltas: 100% (3641/3641), done. + Checking out files: 100% (1847/1847), done. + $ cd swiftmailer/ + $ ls + CHANGES LICENSE.LGPL README.git VERSION docs lib test-suite util + LICENSE.GPL README TODO build.xml examples notes tests + $ + +Uploading to your Host +---------------------- + +You only need to upload the "lib/" directory to your web host for production +use. All other files and directories are support files not needed in +production. + +You will need FTP, ``rsync`` or similar software installed in order to upload +the "lib/" directory to your web host. + +To upload Swift Mailer: + +* Open your FTP program, or a command line if you prefer rsync/scp. + +* Upload the "lib/" directory to your hosting account. + +The files needed to use Swift Mailer should now be accessible to PHP on your +host. + +The following example shows show you can upload the files using +``rsync`` on Linux or OS X. + +.. note:: + + You do not need to place the files inside your web root. They only need to + be in a place where your PHP scripts can "include" them. + + .. code-block:: bash + + $ rsync -rvz lib d11wtq@swiftmailer.org:swiftmailer + building file list ... done + created directory swiftmailer + lib/ + lib/mime_types.php + lib/preferences.php + lib/swift_required.php + lib/classes/ + lib/classes/Swift/ + lib/classes/Swift/Attachment.php + lib/classes/Swift/CharacterReader.php + ... etc etc ... + lib/dependency_maps/ + lib/dependency_maps/cache_deps.php + lib/dependency_maps/mime_deps.php + lib/dependency_maps/transport_deps.php + + sent 151692 bytes received 2974 bytes 5836.45 bytes/sec + total size is 401405 speedup is 2.60 + $ + +.. _`github`: http://github.com/swiftmailer/swiftmailer + +Troubleshooting +--------------- + +Swift Mailer does not work when used with function overloading as implemented +by ``mbstring`` (``mbstring.func_overload`` set to ``2``). A workaround is to +temporarily change the internal encoding to ``ASCII`` when sending an email: + +.. code-block:: php + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) + { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + + // Create your message and send it with Swift Mailer + + if (isset($mbEncoding)) + { + mb_internal_encoding($mbEncoding); + } diff --git a/vendor/swiftmailer/swiftmailer/doc/introduction.rst b/vendor/swiftmailer/swiftmailer/doc/introduction.rst new file mode 100644 index 0000000..39ab034 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/introduction.rst @@ -0,0 +1,135 @@ +Introduction +============ + +Swift Mailer is a component-based library for sending e-mails from PHP +applications. + +Organization of this Book +------------------------- + +This book has been written so that those who need information quickly are able +to find what they need, and those who wish to learn more advanced topics can +read deeper into each chapter. + +The book begins with an overview of Swift Mailer, discussing what's included +in the package and preparing you for the remainder of the book. + +It is possible to read this user guide just like any other book (from +beginning to end). Each chapter begins with a discussion of the contents it +contains, followed by a short code sample designed to give you a head start. +As you get further into a chapter you will learn more about Swift Mailer's +capabilities, but often you will be able to head directly to the topic you +wish to learn about. + +Throughout this book you will be presented with code samples, which most +people should find ample to implement Swift Mailer appropriately in their own +projects. We will also use diagrams where appropriate, and where we believe +readers may find it helpful we will discuss some related theory, including +reference to certain documents you are able to find online. + +Code Samples +------------ + +Code samples presented in this book will be displayed on a different colored +background in a monospaced font. Samples are not to be taken as copy & paste +code snippets. + +Code examples are used through the book to clarify what is written in text. +They will sometimes be usable as-is, but they should always be taken as +outline/pseudo code only. + +A code sample will look like this:: + + class AClass + { + ... + } + + //A Comment + $obj = new AClass($arg1, $arg2, ... ); + + /* A note about another way of doing something + $obj = AClass::newInstance($arg1, $arg2, ... ); + + */ + +The presence of 3 dots ``...`` in a code sample indicates that we have left +out a chunk of the code for brevity, they are not actually part of the code. + +We will often place multi-line comments ``/* ... */`` in the code so that we +can show alternative ways of achieving the same result. + +You should read the code examples given and try to understand them. They are +kept concise so that you are not overwhelmed with information. + +History of Swift Mailer +----------------------- + +Swift Mailer began back in 2005 as a one-class project for sending mail over +SMTP. It has since grown into the flexible component-based library that is in +development today. + +Chris Corbyn first posted Swift Mailer on a web forum asking for comments from +other developers. It was never intended as a fully supported open source +project, but members of the forum began to adopt it and make use of it. + +Very quickly feature requests were coming for the ability to add attachments +and use SMTP authentication, along with a number of other "obvious" missing +features. Considering the only alternative was PHPMailer it seemed like a good +time to bring some fresh tools to the table. Chris began working towards a +more component based, PHP5-like approach unlike the existing single-class, +legacy PHP4 approach taken by PHPMailer. + +Members of the forum offered a lot of advice and critique on the code as he +worked through this project and released versions 2 and 3 of the library in +2005 and 2006, which by then had been broken down into smaller classes +offering more flexibility and supporting plugins. To this day the Swift Mailer +team still receive a lot of feature requests from users both on the forum and +in by email. + +Until 2008 Chris was the sole developer of Swift Mailer, but entering 2009 he +gained the support of two experienced developers well-known to him: Paul +Annesley and Christopher Thompson. This has been an extremely welcome change. + +As of September 2009, Chris handed over the maintenance of Swift Mailer to +Fabien Potencier. + +Now 2009 and in its fourth major version Swift Mailer is more object-oriented +and flexible than ever, both from a usability standpoint and from a +development standpoint. + +By no means is Swift Mailer ready to call "finished". There are still many +features that can be added to the library along with the constant refactoring +that happens behind the scenes. + +It's a Library! +--------------- + +Swift Mailer is not an application - it's a library. + +To most experienced developers this is probably an obvious point to make, but +it's certainly worth mentioning. Many people often contact us having gotten +the completely wrong end of the stick in terms of what Swift Mailer is +actually for. + +It's not an application. It does not have a graphical user interface. It +cannot be opened in your web browser directly. + +It's a library (or a framework if you like). It provides a whole lot of +classes that do some very complicated things, so that you don't have to. You +"use" Swift Mailer within an application so that your application can have the +ability to send emails. + +The component-based structure of the library means that you are free to +implement it in a number of different ways and that you can pick and choose +what you want to use. + +An application on the other hand (such as a blog or a forum) is already "put +together" in a particular way, (usually) provides a graphical user interface +and most likely doesn't offer a great deal of integration with your own +application. + +Embrace the structure of the library and use the components it offers to your +advantage. Learning what the components do, rather than blindly copying and +pasting existing code will put you in a great position to build a powerful +application! diff --git a/vendor/swiftmailer/swiftmailer/doc/messages.rst b/vendor/swiftmailer/swiftmailer/doc/messages.rst new file mode 100644 index 0000000..fd24d31 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/messages.rst @@ -0,0 +1,964 @@ +Creating Messages +================= + +Creating messages in Swift Mailer is done by making use of the various MIME +entities provided with the library. Complex messages can be quickly created +with very little effort. + +Quick Reference for Creating a Message +--------------------------------------- + +You can think of creating a Message as being similar to the steps you perform +when you click the Compose button in your mail client. You give it a subject, +specify some recipients, add any attachments and write your message. + +To create a Message: + +* Call the ``newInstance()`` method of ``Swift_Message``. + +* Set your sender address (``From:``) with ``setFrom()`` or ``setSender()``. + +* Set a subject line with ``setSubject()``. + +* Set recipients with ``setTo()``, ``setCc()`` and/or ``setBcc()``. + +* Set a body with ``setBody()``. + +* Add attachments with ``attach()``. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the message + $message = Swift_Message::newInstance() + + // Give the message a subject + ->setSubject('Your subject') + + // Set the From address with an associative array + ->setFrom(array('john@doe.com' => 'John Doe')) + + // Set the To addresses with an associative array + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + + // Give it a body + ->setBody('Here is the message itself') + + // And optionally an alternative body + ->addPart('Here is the message itself', 'text/html') + + // Optionally add any attachments + ->attach(Swift_Attachment::fromPath('my-document.pdf')) + ; + +Message Basics +-------------- + +A message is a container for anything you want to send to somebody else. There +are several basic aspects of a message that you should know. + +An e-mail message is made up of several relatively simple entities that are +combined in different ways to achieve different results. All of these entities +have the same fundamental outline but serve a different purpose. The Message +itself can be defined as a MIME entity, an Attachment is a MIME entity, all +MIME parts are MIME entities -- and so on! + +The basic units of each MIME entity -- be it the Message itself, or an +Attachment -- are its Headers and its body: + +.. code-block:: text + + Header-Name: A header value + Other-Header: Another value + + The body content itself + +The Headers of a MIME entity, and its body must conform to some strict +standards defined by various RFC documents. Swift Mailer ensures that these +specifications are followed by using various types of object, including +Encoders and different Header types to generate the entity. + +The Structure of a Message +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Of all of the MIME entities, a message -- ``Swift_Message`` +is the largest and most complex. It has many properties that can be updated +and it can contain other MIME entities -- attachments for example -- +nested inside it. + +A Message has a lot of different Headers which are there to present +information about the message to the recipients' mail client. Most of these +headers will be familiar to the majority of users, but we'll list the basic +ones. Although it's possible to work directly with the Headers of a Message +(or other MIME entity), the standard Headers have accessor methods provided to +abstract away the complex details for you. For example, although the Date on a +message is written with a strict format, you only need to pass a UNIX +timestamp to ``setDate()``. + ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| Header | Description | Accessors | ++===============================+====================================================================================================================================+=============================================+ +| ``Message-ID`` | Identifies this message with a unique ID, usually containing the domain name and time generated | ``getId()`` / ``setId()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Return-Path`` | Specifies where bounces should go (Swift Mailer reads this for other uses) | ``getReturnPath()`` / ``setReturnPath()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``From`` | Specifies the address of the person who the message is from. This can be multiple addresses if multiple people wrote the message. | ``getFrom()`` / ``setFrom()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Sender`` | Specifies the address of the person who physically sent the message (higher precedence than ``From:``) | ``getSender()`` / ``setSender()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``To`` | Specifies the addresses of the intended recipients | ``getTo()`` / ``setTo()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Cc`` | Specifies the addresses of recipients who will be copied in on the message | ``getCc()`` / ``setCc()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Bcc`` | Specifies the addresses of recipients who the message will be blind-copied to. Other recipients will not be aware of these copies. | ``getBcc()`` / ``setBcc()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Reply-To`` | Specifies the address where replies are sent to | ``getReplyTo()`` / ``setReplyTo()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Subject`` | Specifies the subject line that is displayed in the recipients' mail client | ``getSubject()`` / ``setSubject()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Date`` | Specifies the date at which the message was sent | ``getDate()`` / ``setDate()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Content-Type`` | Specifies the format of the message (usually text/plain or text/html) | ``getContentType()`` / ``setContentType()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Content-Transfer-Encoding`` | Specifies the encoding scheme in the message | ``getEncoder()`` / ``setEncoder()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ + +Working with a Message Object +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Although there are a lot of available methods on a message object, you only +need to make use of a small subset of them. Usually you'll use +``setSubject()``, ``setTo()`` and +``setFrom()`` before setting the body of your message with +``setBody()``. + +Calling methods is simple. You just call them like functions, but using the +object operator "``]]>``" to do so. If you've created +a message object and called it ``$message`` then you'd set a +subject on it like so: + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + $message = Swift_Message::newInstance(); + $message->setSubject('My subject'); + +All MIME entities (including a message) have a ``toString()`` +method that you can call if you want to take a look at what is going to be +sent. For example, if you ``toString();]]>`` you would see something like this: + +.. code-block:: bash + + Message-ID: <1230173678.4952f5eeb1432@swift.generated> + Date: Thu, 25 Dec 2008 13:54:38 +1100 + Subject: Example subject + From: Chris Corbyn + To: Receiver Name + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: quoted-printable + + Here is the message + +We'll take a closer look at the methods you use to create your message in the +following sections. + +Adding Content to Your Message +------------------------------ + +Rich content can be added to messages in Swift Mailer with relative ease by +calling methods such as ``setSubject()``, ``setBody()``, ``addPart()`` and +``attach()``. + +Setting the Subject Line +~~~~~~~~~~~~~~~~~~~~~~~~ + +The subject line, displayed in the recipients' mail client can be set with the +``setSubject()`` method, or as a parameter to ``Swift_Message::newInstance()``. + +To set the subject of your Message: + +* Call the ``setSubject()`` method of the Message, or specify it at the time + you create the message. + + .. code-block:: php + + // Pass it as a parameter when you create the message + $message = Swift_Message::newInstance('My amazing subject'); + + // Or set it after like this + $message->setSubject('My amazing subject'); + +Setting the Body Content +~~~~~~~~~~~~~~~~~~~~~~~~ + +The body of the message -- seen when the user opens the message -- +is specified by calling the ``setBody()`` method. If an alternative body is to +be included ``addPart()`` can be used. + +The body of a message is the main part that is read by the user. Often people +want to send a message in HTML format (``text/html``), other +times people want to send in plain text (``text/plain``), or +sometimes people want to send both versions and allow the recipient to chose +how they view the message. + +As a rule of thumb, if you're going to send a HTML email, always include a +plain-text equivalent of the same content so that users who prefer to read +plain text can do so. + +To set the body of your Message: + +* Call the ``setBody()`` method of the Message, or specify it at the time you + create the message. + +* Add any alternative bodies with ``addPart()``. + +If the recipient's mail client offers preferences for displaying text vs. HTML +then the mail client will present that part to the user where available. In +other cases the mail client will display the "best" part it can - usually HTML +if you've included HTML. + +.. code-block:: php + + // Pass it as a parameter when you create the message + $message = Swift_Message::newInstance('Subject here', 'My amazing body'); + + // Or set it after like this + $message->setBody('My amazing body', 'text/html'); + + // Add alternative parts with addPart() + $message->addPart('My amazing body in plain text', 'text/plain'); + +Attaching Files +--------------- + +Attachments are downloadable parts of a message and can be added by calling +the ``attach()`` method on the message. You can add attachments that exist on +disk, or you can create attachments on-the-fly. + +Attachments are actually an interesting area of Swift Mailer and something +that could put a lot of power at your fingertips if you grasp the concept +behind the way a message is held together. + +Although we refer to files sent over e-mails as "attachments" -- because +they're attached to the message -- lots of other parts of the message are +actually "attached" even if we don't refer to these parts as attachments. + +File attachments are created by the ``Swift_Attachment`` class +and then attached to the message via the ``attach()`` method on +it. For all of the "every day" MIME types such as all image formats, word +documents, PDFs and spreadsheets you don't need to explicitly set the +content-type of the attachment, though it would do no harm to do so. For less +common formats you should set the content-type -- which we'll cover in a +moment. + +Attaching Existing Files +~~~~~~~~~~~~~~~~~~~~~~~~ + +Files that already exist, either on disk or at a URL can be attached to a +message with just one line of code, using ``Swift_Attachment::fromPath()``. + +You can attach files that exist locally, or if your PHP installation has +``allow_url_fopen`` turned on you can attach files from other +websites. + +To attach an existing file: + +* Create an attachment with ``Swift_Attachment::fromPath()``. + +* Add the attachment to the message with ``attach()``. + +The attachment will be presented to the recipient as a downloadable file with +the same filename as the one you attached. + +.. code-block:: php + + // Create the attachment + // * Note that you can technically leave the content-type parameter out + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg', 'image/jpeg'); + + // Attach it to the message + $message->attach($attachment); + + + // The two statements above could be written in one line instead + $message->attach(Swift_Attachment::fromPath('/path/to/image.jpg')); + + + // You can attach files from a URL if allow_url_fopen is on in php.ini + $message->attach(Swift_Attachment::fromPath('http://site.tld/logo.png')); + +Setting the Filename +~~~~~~~~~~~~~~~~~~~~ + +Usually you don't need to explicitly set the filename of an attachment because +the name of the attached file will be used by default, but if you want to set +the filename you use the ``setFilename()`` method of the Attachment. + +To change the filename of an attachment: + +* Call its ``setFilename()`` method. + +The attachment will be attached in the normal way, but meta-data sent inside +the email will rename the file to something else. + +.. code-block:: php + + // Create the attachment and call its setFilename() method + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg') + ->setFilename('cool.jpg'); + + + // Because there's a fluid interface, you can do this in one statement + $message->attach( + Swift_Attachment::fromPath('/path/to/image.jpg')->setFilename('cool.jpg') + ); + +Attaching Dynamic Content +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Files that are generated at runtime, such as PDF documents or images created +via GD can be attached directly to a message without writing them out to disk. +Use the standard ``Swift_Attachment::newInstance()`` method. + +To attach dynamically created content: + +* Create your content as you normally would. + +* Create an attachment with ``Swift_Attachment::newInstance()``, specifying + the source data of your content along with a name and the content-type. + +* Add the attachment to the message with ``attach()``. + +The attachment will be presented to the recipient as a downloadable file +with the filename and content-type you specify. + +.. note:: + + If you would usually write the file to disk anyway you should just attach + it with ``Swift_Attachment::fromPath()`` since this will use less memory: + + .. code-block:: php + + // Create your file contents in the normal way, but don't write them to disk + $data = create_my_pdf_data(); + + // Create the attachment with your data + $attachment = Swift_Attachment::newInstance($data, 'my-file.pdf', 'application/pdf'); + + // Attach it to the message + $message->attach($attachment); + + + // You can alternatively use method chaining to build the attachment + $attachment = Swift_Attachment::newInstance() + ->setFilename('my-file.pdf') + ->setContentType('application/pdf') + ->setBody($data) + ; + +Changing the Disposition +~~~~~~~~~~~~~~~~~~~~~~~~ + +Attachments just appear as files that can be saved to the Desktop if desired. +You can make attachment appear inline where possible by using the +``setDisposition()`` method of an attachment. + +To make an attachment appear inline: + +* Call its ``setDisposition()`` method. + +The attachment will be displayed within the email viewing window if the mail +client knows how to display it. + +.. note:: + + If you try to create an inline attachment for a non-displayable file type + such as a ZIP file, the mail client should just present the attachment as + normal: + + .. code-block:: php + + // Create the attachment and call its setDisposition() method + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg') + ->setDisposition('inline'); + + + // Because there's a fluid interface, you can do this in one statement + $message->attach( + Swift_Attachment::fromPath('/path/to/image.jpg')->setDisposition('inline') + ); + +Embedding Inline Media Files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Often people want to include an image or other content inline with a HTML +message. It's easy to do this with HTML linking to remote resources, but this +approach is usually blocked by mail clients. Swift Mailer allows you to embed +your media directly into the message. + +Mail clients usually block downloads from remote resources because this +technique was often abused as a mean of tracking who opened an email. If +you're sending a HTML email and you want to include an image in the message +another approach you can take is to embed the image directly. + +Swift Mailer makes embedding files into messages extremely streamlined. You +embed a file by calling the ``embed()`` method of the message, +which returns a value you can use in a ``src`` or +``href`` attribute in your HTML. + +Just like with attachments, it's possible to embed dynamically generated +content without having an existing file available. + +The embedded files are sent in the email as a special type of attachment that +has a unique ID used to reference them within your HTML attributes. On mail +clients that do not support embedded files they may appear as attachments. + +Although this is commonly done for images, in theory it will work for any +displayable (or playable) media type. Support for other media types (such as +video) is dependent on the mail client however. + +Embedding Existing Files +........................ + +Files that already exist, either on disk or at a URL can be embedded in a +message with just one line of code, using ``Swift_EmbeddedFile::fromPath()``. + +You can embed files that exist locally, or if your PHP installation has +``allow_url_fopen`` turned on you can embed files from other websites. + +To embed an existing file: + +* Create a message object with ``Swift_Message::newInstance()``. + +* Set the body as HTML, and embed a file at the correct point in the message with ``embed()``. + +The file will be displayed with the message inline with the HTML wherever its ID +is used as a ``src`` attribute. + +.. note:: + + ``Swift_Image`` and ``Swift_EmbeddedFile`` are just aliases of one + another. ``Swift_Image`` exists for semantic purposes. + +.. note:: + + You can embed files in two stages if you prefer. Just capture the return + value of ``embed()`` in a variable and use that as the ``src`` attribute. + + .. code-block:: php + + // Create the message + $message = Swift_Message::newInstance('My subject'); + + // Set the body + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + + // You can embed files from a URL if allow_url_fopen is on in php.ini + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' + ); + + + // If placing the embed() code inline becomes cumbersome + // it's easy to do this in two steps + $cid = $message->embed(Swift_Image::fromPath('image.png')); + + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + +Embedding Dynamic Content +......................... + +Images that are generated at runtime, such as images created via GD can be +embedded directly to a message without writing them out to disk. Use the +standard ``Swift_Image::newInstance()`` method. + +To embed dynamically created content: + +* Create a message object with ``Swift_Message::newInstance()``. + +* Set the body as HTML, and embed a file at the correct point in the message + with ``embed()``. You will need to specify a filename and a content-type. + +The file will be displayed with the message inline with the HTML wherever its ID +is used as a ``src`` attribute. + +.. note:: + + ``Swift_Image`` and ``Swift_EmbeddedFile`` are just aliases of one + another. ``Swift_Image`` exists for semantic purposes. + +.. note:: + + You can embed files in two stages if you prefer. Just capture the return + value of ``embed()`` in a variable and use that as the ``src`` attribute. + + .. code-block:: php + + // Create your file contents in the normal way, but don't write them to disk + $img_data = create_my_image_data(); + + //Create the message + $message = Swift_Message::newInstance('My subject'); + + //Set the body + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + + + // If placing the embed() code inline becomes cumbersome + // it's easy to do this in two steps + $cid = $message->embed(Swift_Image::newInstance($img_data, 'image.jpg', 'image/jpeg')); + + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + +Adding Recipients to Your Message +--------------------------------- + +Recipients are specified within the message itself via ``setTo()``, ``setCc()`` +and ``setBcc()``. Swift Mailer reads these recipients from the message when it +gets sent so that it knows where to send the message to. + +Message recipients are one of three types: + +* ``To:`` recipients -- the primary recipients (required) + +* ``Cc:`` recipients -- receive a copy of the message (optional) + +* ``Bcc:`` recipients -- hidden from other recipients (optional) + +Each type can contain one, or several addresses. It's possible to list only +the addresses of the recipients, or you can personalize the address by +providing the real name of the recipient. + +.. sidebar:: Syntax for Addresses + + If you only wish to refer to a single email address (for example your + ``From:`` address) then you can just use a string. + + .. code-block:: php + + $message->setFrom('some@address.tld'); + + If you want to include a name then you must use an associative array. + + .. code-block:: php + + $message->setFrom(array('some@address.tld' => 'The Name')); + + If you want to include multiple addresses then you must use an array. + + .. code-block:: php + + $message->setTo(array('some@address.tld', 'other@address.tld')); + + You can mix personalized (addresses with a name) and non-personalized + addresses in the same list by mixing the use of associative and + non-associative array syntax. + + .. code-block:: php + + $message->setTo(array( + 'recipient-with-name@example.org' => 'Recipient Name One', + 'no-name@example.org', // Note that this is not a key-value pair + 'named-recipient@example.org' => 'Recipient Name Two' + )); + +Setting ``To:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``To:`` recipients are required in a message and are set with the +``setTo()`` or ``addTo()`` methods of the message. + +To set ``To:`` recipients, create the message object using either +``new Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, +then call the ``setTo()`` method with a complete array of addresses, or use the +``addTo()`` method to iteratively add recipients. + +The ``setTo()`` method accepts input in various formats as described earlier in +this chapter. The ``addTo()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +``To:`` recipients are visible in the message headers and will be +seen by the other recipients. + +.. note:: + + Multiple calls to ``setTo()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add + recipients, use the ``addTo()`` method. + + .. code-block:: php + + // Using setTo() to set all recipients in one go + $message->setTo(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addTo() to add recipients iteratively + $message->addTo('person1@example.org'); + $message->addTo('person2@example.org', 'Person 2 Name'); + +Setting ``Cc:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Cc:`` recipients are set with the ``setCc()`` or ``addCc()`` methods of the +message. + +To set ``Cc:`` recipients, create the message object using either +``new Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, then call +the ``setCc()`` method with a complete array of addresses, or use the +``addCc()`` method to iteratively add recipients. + +The ``setCc()`` method accepts input in various formats as described earlier in +this chapter. The ``addCc()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +``Cc:`` recipients are visible in the message headers and will be +seen by the other recipients. + +.. note:: + + Multiple calls to ``setCc()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add Cc: + recipients, use the ``addCc()`` method. + + .. code-block:: php + + // Using setCc() to set all recipients in one go + $message->setCc(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addCc() to add recipients iteratively + $message->addCc('person1@example.org'); + $message->addCc('person2@example.org', 'Person 2 Name'); + +Setting ``Bcc:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Bcc:`` recipients receive a copy of the message without anybody else knowing +it, and are set with the ``setBcc()`` or ``addBcc()`` methods of the message. + +To set ``Bcc:`` recipients, create the message object using either ``new +Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, then call the +``setBcc()`` method with a complete array of addresses, or use +the ``addBcc()`` method to iteratively add recipients. + +The ``setBcc()`` method accepts input in various formats as described earlier in +this chapter. The ``addBcc()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +Only the individual ``Bcc:`` recipient will see their address in the message +headers. Other recipients (including other ``Bcc:`` recipients) will not see the +address. + +.. note:: + + Multiple calls to ``setBcc()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add Bcc: + recipients, use the ``addBcc()`` method. + + .. code-block:: php + + // Using setBcc() to set all recipients in one go + $message->setBcc(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addBcc() to add recipients iteratively + $message->addBcc('person1@example.org'); + $message->addBcc('person2@example.org', 'Person 2 Name'); + +Specifying Sender Details +------------------------- + +An email must include information about who sent it. Usually this is managed +by the ``From:`` address, however there are other options. + +The sender information is contained in three possible places: + +* ``From:`` -- the address(es) of who wrote the message (required) + +* ``Sender:`` -- the address of the single person who sent the message + (optional) + +* ``Return-Path:`` -- the address where bounces should go to (optional) + +You must always include a ``From:`` address by using ``setFrom()`` on the +message. Swift Mailer will use this as the default ``Return-Path:`` unless +otherwise specified. + +The ``Sender:`` address exists because the person who actually sent the email +may not be the person who wrote the email. It has a higher precedence than the +``From:`` address and will be used as the ``Return-Path:`` unless otherwise +specified. + +Setting the ``From:`` Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``From:`` address is required and is set with the ``setFrom()`` method of the +message. ``From:`` addresses specify who actually wrote the email, and usually who sent it. + +What most people probably don't realise is that you can have more than one +``From:`` address if more than one person wrote the email -- for example if an +email was put together by a committee. + +To set the ``From:`` address(es): + +* Call the ``setFrom()`` method on the Message. + +The ``From:`` address(es) are visible in the message headers and +will be seen by the recipients. + +.. note:: + + If you set multiple ``From:`` addresses then you absolutely must set a + ``Sender:`` address to indicate who physically sent the message. + + .. code-block:: php + + // Set a single From: address + $message->setFrom('your@address.tld'); + + // Set a From: address including a name + $message->setFrom(array('your@address.tld' => 'Your Name')); + + // Set multiple From: addresses if multiple people wrote the email + $message->setFrom(array( + 'person1@example.org' => 'Sender One', + 'person2@example.org' => 'Sender Two' + )); + +Setting the ``Sender:`` Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``Sender:`` address specifies who sent the message and is set with the +``setSender()`` method of the message. + +To set the ``Sender:`` address: + +* Call the ``setSender()`` method on the Message. + +The ``Sender:`` address is visible in the message headers and will be seen by +the recipients. + +This address will be used as the ``Return-Path:`` unless otherwise specified. + +.. note:: + + If you set multiple ``From:`` addresses then you absolutely must set a + ``Sender:`` address to indicate who physically sent the message. + +You must not set more than one sender address on a message because it's not +possible for more than one person to send a single message. + +.. code-block:: php + + $message->setSender('your@address.tld'); + +Setting the ``Return-Path:`` (Bounce) Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Return-Path:`` address specifies where bounce notifications should +be sent and is set with the ``setReturnPath()`` method of the message. + +You can only have one ``Return-Path:`` and it must not include +a personal name. + +To set the ``Return-Path:`` address: + +* Call the ``setReturnPath()`` method on the Message. + +Bouce notifications will be sent to this address. + +.. code-block:: php + + $message->setReturnPath('bounces@address.tld'); + +Requesting a Read Receipt +------------------------- + +It is possible to request a read-receipt to be sent to an address when the +email is opened. To request a read receipt set the address with +``setReadReceiptTo()``. + +To request a read receipt: + +* Set the address you want the receipt to be sent to with the + ``setReadReceiptTo()`` method on the Message. + +When the email is opened, if the mail client supports it a notification will be sent to this address. + +.. note:: + + Read receipts won't work for the majority of recipients since many mail + clients auto-disable them. Those clients that will send a read receipt + will make the user aware that one has been requested. + + .. code-block:: php + + $message->setReadReceiptTo('your@address.tld'); + +Setting the Character Set +------------------------- + +The character set of the message (and it's MIME parts) is set with the +``setCharset()`` method. You can also change the global default of UTF-8 by +working with the ``Swift_Preferences`` class. + +Swift Mailer will default to the UTF-8 character set unless otherwise +overridden. UTF-8 will work in most instances since it includes all of the +standard US keyboard characters in addition to most international characters. + +It is absolutely vital however that you know what character set your message +(or it's MIME parts) are written in otherwise your message may be received +completely garbled. + +There are two places in Swift Mailer where you can change the character set: + +* In the ``Swift_Preferences`` class + +* On each individual message and/or MIME part + +To set the character set of your Message: + +* Change the global UTF-8 setting by calling + ``Swift_Preferences::setCharset()``; or + +* Call the ``setCharset()`` method on the message or the MIME part. + + .. code-block:: php + + // Approach 1: Change the global setting (suggested) + Swift_Preferences::getInstance()->setCharset('iso-8859-2'); + + // Approach 2: Call the setCharset() method of the message + $message = Swift_Message::newInstance() + ->setCharset('iso-8859-2'); + + // Apprach 3: Specify the charset when setting the body + $message->setBody('My body', 'text/html', 'iso-8859-2'); + + // Approach 4: Specify the charset for each part added + $message->addPart('My part', 'text/plain', 'iso-8859-2'); + +Setting the Line Length +----------------------- + +The length of lines in a message can be changed by using the ``setMaxLineLength()`` method on the message. It should be kept to less than +1000 characters. + +Swift Mailer defaults to using 78 characters per line in a message. This is +done for historical reasons and so that the message can be easily viewed in +plain-text terminals. + +To change the maximum length of lines in your Message: + +* Call the ``setMaxLineLength()`` method on the Message. + +Lines that are longer than the line length specified will be wrapped between +words. + +.. note:: + + You should never set a maximum length longer than 1000 characters + according to RFC 2822. Doing so could have unspecified side-effects such + as truncating parts of your message when it is transported between SMTP + servers. + + .. code-block:: php + + $message->setMaxLineLength(1000); + +Setting the Message Priority +---------------------------- + +You can change the priority of the message with ``setPriority()``. Setting the +priority will not change the way your email is sent -- it is purely an +indicative setting for the recipient. + +The priority of a message is an indication to the recipient what significance +it has. Swift Mailer allows you to set the priority by calling the ``setPriority`` method. This method takes an integer value between 1 and 5: + +* Highest +* High +* Normal +* Low +* Lowest + +To set the message priority: + +* Set the priority as an integer between 1 and 5 with the ``setPriority()`` + method on the Message. + +.. code-block:: php + + // Indicate "High" priority + $message->setPriority(2); diff --git a/vendor/swiftmailer/swiftmailer/doc/overview.rst b/vendor/swiftmailer/swiftmailer/doc/overview.rst new file mode 100644 index 0000000..c912617 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/overview.rst @@ -0,0 +1,161 @@ +Library Overview +================ + +Most features (and more) of your every day mail client software are provided +by Swift Mailer, using object-oriented PHP code as the interface. + +In this chapter we will take a short tour of the various components, which put +together form the Swift Mailer library as a whole. You will learn key +terminology used throughout the rest of this book and you will gain a little +understanding of the classes you will work with as you integrate Swift Mailer +into your application. + +This chapter is intended to prepare you for the information contained in the +subsequent chapters of this book. You may choose to skip this chapter if you +are fairly technically minded, though it is likely to save you some time in +the long run if you at least read between the lines here. + +System Requirements +------------------- + +The basic requirements to operate Swift Mailer are extremely minimal and +easily achieved. Historically, Swift Mailer has supported both PHP 4 and PHP 5 +by following a parallel development workflow. Now in it's fourth major +version, and Swift Mailer operates on servers running PHP 5.2 or higher. + +The library aims to work with as many PHP 5 projects as possible: + +* PHP 5.2 or higher, with the SPL extension (standard) + +* Limited network access to connect to remote SMTP servers + +* 8 MB or more memory limit (Swift Mailer uses around 2 MB) + +Component Breakdown +------------------- + +Swift Mailer is made up of many classes. Each of these classes can be grouped +into a general "component" group which describes the task it is designed to +perform. + +We'll take a brief look at the components which form Swift Mailer in this +section of the book. + +The Mailer +~~~~~~~~~~ + +The mailer class, ``Swift_Mailer`` is the central class in the library where +all of the other components meet one another. ``Swift_Mailer`` acts as a sort +of message dispatcher, communicating with the underlying Transport to deliver +your Message to all intended recipients. + +If you were to dig around in the source code for Swift Mailer you'd notice +that ``Swift_Mailer`` itself is pretty bare. It delegates to other objects for +most tasks and in theory, if you knew the internals of Swift Mailer well you +could by-pass this class entirely. We wouldn't advise doing such a thing +however -- there are reasons this class exists: + +* for consistency, regardless of the Transport used + +* to provide abstraction from the internals in the event internal API changes + are made + +* to provide convenience wrappers around aspects of the internal API + +An instance of ``Swift_Mailer`` is created by the developer before sending any +Messages. + +Transports +~~~~~~~~~~ + +Transports are the classes in Swift Mailer that are responsible for +communicating with a service in order to deliver a Message. There are several +types of Transport in Swift Mailer, all of which implement the Swift_Transport +interface and offer underlying start(), stop() and send() methods. + +Typically you will not need to know how a Transport works under-the-surface, +you will only need to know how to create an instance of one, and which one to +use for your environment. + ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| Class | Features | Pros/cons | ++=================================+=============================================================================================+===============================================================================================================================================+ +| ``Swift_SmtpTransport`` | Sends messages over SMTP; Supports Authentication; Supports Encryption | Very portable; Pleasingly predictable results; Provides good feedback | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_SendmailTransport`` | Communicates with a locally installed ``sendmail`` executable (Linux/UNIX) | Quick time-to-run; Provides less-accurate feedback than SMTP; Requires ``sendmail`` installation | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_MailTransport`` | Uses PHP's built-in ``mail()`` function | Very portable; Potentially unpredictable results; Provides extremely weak feedback | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_LoadBalancedTransport`` | Cycles through a collection of the other Transports to manage load-reduction | Provides graceful fallback if one Transport fails (e.g. an SMTP server is down); Keeps the load on remote services down by spreading the work | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_FailoverTransport`` | Works in conjunction with a collection of the other Transports to provide high-availability | Provides graceful fallback if one Transport fails (e.g. an SMTP server is down) | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ + +MIME Entities +~~~~~~~~~~~~~ + +Everything that forms part of a Message is called a MIME Entity. All MIME +entities in Swift Mailer share a common set of features. There are various +types of MIME entity that serve different purposes such as Attachments and +MIME parts. + +An e-mail message is made up of several relatively simple entities that are +combined in different ways to achieve different results. All of these entities +have the same fundamental outline but serve a different purpose. The Message +itself can be defined as a MIME entity, an Attachment is a MIME entity, all +MIME parts are MIME entities -- and so on! + +The basic units of each MIME entity -- be it the Message itself, or an +Attachment -- are its Headers and its body: + +.. code-block:: text + + Other-Header: Another value + + The body content itself + +The Headers of a MIME entity, and its body must conform to some strict +standards defined by various RFC documents. Swift Mailer ensures that these +specifications are followed by using various types of object, including +Encoders and different Header types to generate the entity. + +Each MIME component implements the base ``Swift_Mime_MimeEntity`` interface, +which offers methods for retrieving Headers, adding new Headers, changing the +Encoder, updating the body and so on! + +All MIME entities have one Header in common -- the Content-Type Header, +updated with the entity's ``setContentType()`` method. + +Encoders +~~~~~~~~ + +Encoders are used to transform the content of Messages generated in Swift +Mailer into a format that is safe to send across the internet and that +conforms to RFC specifications. + +Generally speaking you will not need to interact with the Encoders in Swift +Mailer -- the correct settings will be handled by the library itself. +However they are probably worth a brief mention in the event that you do want +to play with them. + +Both the Headers and the body of all MIME entities (including the Message +itself) use Encoders to ensure the data they contain can be sent over the +internet without becoming corrupted or misinterpreted. + +There are two types of Encoder: Base64 and Quoted-Printable. + +Plugins +~~~~~~~ + +Plugins exist to extend, or modify the behaviour of Swift Mailer. They respond +to Events that are fired within the Transports during sending. + +There are a number of Plugins provided as part of the base Swift Mailer +package and they all follow a common interface to respond to Events fired +within the library. Interfaces are provided to "listen" to each type of Event +fired and to act as desired when a listened-to Event occurs. + +Although several plugins are provided with Swift Mailer out-of-the-box, the +Events system has been specifically designed to make it easy for experienced +object-oriented developers to write their own plugins in order to achieve +goals that may not be possible with the base library. diff --git a/vendor/swiftmailer/swiftmailer/doc/plugins.rst b/vendor/swiftmailer/swiftmailer/doc/plugins.rst new file mode 100644 index 0000000..e423476 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/plugins.rst @@ -0,0 +1,385 @@ +Plugins +======= + +Plugins are provided with Swift Mailer and can be used to extend the behavior +of the library in ways that simple class inheritance would be more complex. + +AntiFlood Plugin +---------------- + +Many SMTP servers have limits on the number of messages that may be sent +during any single SMTP connection. The AntiFlood plugin provides a way to stay +within this limit while still managing a large number of emails. + +A typical limit for a single connection is 100 emails. If the server you +connect to imposes such a limit, it expects you to disconnect after that +number of emails has been sent. You could manage this manually within a loop, +but the AntiFlood plugin provides the necessary wrapper code so that you don't +need to worry about this logic. + +Regardless of limits imposed by the server, it's usually a good idea to be +conservative with the resources of the SMTP server. Sending will become +sluggish if the server is being over-used so using the AntiFlood plugin will +not be a bad idea even if no limits exist. + +The AntiFlood plugin's logic is basically to disconnect and the immediately +re-connect with the SMTP server every X number of emails sent, where X is a +number you specify to the plugin. + +You can also specify a time period in seconds that Swift Mailer should pause +for between the disconnect/re-connect process. It's a good idea to pause for a +short time (say 30 seconds every 100 emails) simply to give the SMTP server a +chance to process its queue and recover some resources. + +Using the AntiFlood Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The AntiFlood Plugin -- like all plugins -- is added with the Mailer class' +``registerPlugin()`` method. It takes two constructor parameters: the number of +emails to pause after, and optionally the number of seconds to pause for. + +To use the AntiFlood plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the ``Swift_Plugins_AntiFloodPlugin`` class, passing + in one or two constructor parameters. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +When Swift Mailer sends messages it will count the number of messages that +have been sent since the last re-connect. Once the number hits your specified +threshold it will disconnect and re-connect, optionally pausing for a +specified amount of time. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // Use AntiFlood to re-connect after 100 emails + $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100)); + + // And specify a time in seconds to pause for (30 secs) + $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100, 30)); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + +Throttler Plugin +---------------- + +If your SMTP server has restrictions in place to limit the rate at which you +send emails, then your code will need to be aware of this rate-limiting. The +Throttler plugin makes Swift Mailer run at a rate-limited speed. + +Many shared hosts don't open their SMTP servers as a free-for-all. Usually +they have policies in place (probably to discourage spammers) that only allow +you to send a fixed number of emails per-hour/day. + +The Throttler plugin supports two modes of rate-limiting and with each, you +will need to do that math to figure out the values you want. The plugin can +limit based on the number of emails per minute, or the number of +bytes-transferred per-minute. + +Using the Throttler Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Throttler Plugin -- like all plugins -- is added with the Mailer class' +``registerPlugin()`` method. It has two required constructor parameters that +tell it how to do its rate-limiting. + +To use the Throttler plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the ``Swift_Plugins_ThrottlerPlugin`` class, passing + the number of emails, or bytes you wish to limit by, along with the mode + you're using. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +When Swift Mailer sends messages it will keep track of the rate at which sending +messages is occurring. If it realises that sending is happening too fast, it +will cause your program to ``sleep()`` for enough time to average out the rate. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // Rate limit to 100 emails per-minute + $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin( + 100, Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE + )); + + // Rate limit to 10MB per-minute + $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin( + 1024 * 1024 * 10, Swift_Plugins_ThrottlerPlugin::BYTES_PER_MINUTE + )); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + +Logger Plugin +------------- + +The Logger plugins helps with debugging during the process of sending. It can +help to identify why an SMTP server is rejecting addresses, or any other +hard-to-find problems that may arise. + +The Logger plugin comes in two parts. There's the plugin itself, along with +one of a number of possible Loggers that you may choose to use. For example, +the logger may output messages directly in realtime, or it may capture +messages in an array. + +One other notable feature is the way in which the Logger plugin changes +Exception messages. If Exceptions are being thrown but the error message does +not provide conclusive information as to the source of the problem (such as an +ambiguous SMTP error) the Logger plugin includes the entire SMTP transcript in +the error message so that debugging becomes a simpler task. + +There are a few available Loggers included with Swift Mailer, but writing your +own implementation is incredibly simple and is achieved by creating a short +class that implements the ``Swift_Plugins_Logger`` interface. + +* ``Swift_Plugins_Loggers_ArrayLogger``: Keeps a collection of log messages + inside an array. The array content can be cleared or dumped out to the + screen. + +* ``Swift_Plugins_Loggers_EchoLogger``: Prints output to the screen in + realtime. Handy for very rudimentary debug output. + +Using the Logger Plugin +~~~~~~~~~~~~~~~~~~~~~~~ + +The Logger Plugin -- like all plugins -- is added with the Mailer class' +``registerPlugin()`` method. It accepts an instance of ``Swift_Plugins_Logger`` +in its constructor. + +To use the Logger plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the a Logger implementation of + ``Swift_Plugins_Logger``. + +* Create an instance of the ``Swift_Plugins_LoggerPlugin`` class, passing the + created Logger instance to its constructor. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +* Dump the contents of the log with the logger's ``dump()`` method. + +When Swift Mailer sends messages it will keep a log of all the interactions +with the underlying Transport being used. Depending upon the Logger that has +been used the behaviour will differ, but all implementations offer a way to +get the contents of the log. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // To use the ArrayLogger + $logger = new Swift_Plugins_Loggers_ArrayLogger(); + $mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($logger)); + + // Or to use the Echo Logger + $logger = new Swift_Plugins_Loggers_EchoLogger(); + $mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($logger)); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + + // Dump the log contents + // NOTE: The EchoLogger dumps in realtime so dump() does nothing for it + echo $logger->dump(); + +Decorator Plugin +---------------- + +Often there's a need to send the same message to multiple recipients, but with +tiny variations such as the recipient's name being used inside the message +body. The Decorator plugin aims to provide a solution for allowing these small +differences. + +The decorator plugin works by intercepting the sending process of Swift +Mailer, reading the email address in the To: field and then looking up a set +of replacements for a template. + +While the use of this plugin is simple, it is probably the most commonly +misunderstood plugin due to the way in which it works. The typical mistake +users make is to try registering the plugin multiple times (once for each +recipient) -- inside a loop for example. This is incorrect. + +The Decorator plugin should be registered just once, but containing the list +of all recipients prior to sending. It will use this list of recipients to +find the required replacements during sending. + +Using the Decorator Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the Decorator plugin, simply create an associative array of replacements +based on email addresses and then use the mailer's ``registerPlugin()`` method +to add the plugin. + +First create an associative array of replacements based on the email addresses +you'll be sending the message to. + +.. note:: + + The replacements array becomes a 2-dimensional array whose keys are the + email addresses and whose values are an associative array of replacements + for that email address. The curly braces used in this example can be any + type of syntax you choose, provided they match the placeholders in your + email template. + + .. code-block:: php + + $replacements = array(); + foreach ($users as $user) { + $replacements[$user['email']] = array( + '{username}'=>$user['username'], + '{password}'=>$user['password'] + ); + } + +Now create an instance of the Decorator plugin using this array of replacements +and then register it with the Mailer. Do this only once! + +.. code-block:: php + + $decorator = new Swift_Plugins_DecoratorPlugin($replacements); + + $mailer->registerPlugin($decorator); + +When you create your message, replace elements in the body (and/or the subject +line) with your placeholders. + +.. code-block:: php + + $message = Swift_Message::newInstance() + ->setSubject('Important notice for {username}') + ->setBody( + "Hello {username}, we have reset your password to {password}\n" . + "Please log in and change it at your earliest convenience." + ) + ; + + foreach ($users as $user) { + $message->addTo($user['email']); + } + +When you send this message to each of your recipients listed in your +``$replacements`` array they will receive a message customized for just +themselves. For example, the message used above when received may appear like +this to one user: + +.. code-block:: text + + Subject: Important notice for smilingsunshine2009 + + Hello smilingsunshine2009, we have reset your password to rainyDays + Please log in and change it at your earliest convenience. + +While another use may receive the message as: + +.. code-block:: text + + Subject: Important notice for billy-bo-bob + + Hello billy-bo-bob, we have reset your password to dancingOctopus + Please log in and change it at your earliest convenience. + +While the decorator plugin provides a means to solve this problem, there are +various ways you could tackle this problem without the need for a plugin. +We're trying to come up with a better way ourselves and while we have several +(obvious) ideas we don't quite have the perfect solution to go ahead and +implement it. Watch this space. + +Providing Your Own Replacements Lookup for the Decorator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Filling an array with replacements may not be the best solution for providing +replacement information to the decorator. If you have a more elegant algorithm +that performs replacement lookups on-the-fly you may provide your own +implementation. + +Providing your own replacements lookup implementation for the Decorator is +simply a matter of passing an instance of ``Swift_Plugins_Decorator_Replacements`` to the decorator plugin's constructor, +rather than passing in an array. + +The Replacements interface is very simple to implement since it has just one +method: ``getReplacementsFor($address)``. + +Imagine you want to look up replacements from a database on-the-fly, you might +provide an implementation that does this. You need to create a small class. + +.. code-block:: php + + class DbReplacements implements Swift_Plugins_Decorator_Replacements { + public function getReplacementsFor($address) { + $sql = sprintf( + "SELECT * FROM user WHERE email = '%s'", + mysql_real_escape_string($address) + ); + + $result = mysql_query($sql); + + if ($row = mysql_fetch_assoc($result)) { + return array( + '{username}'=>$row['username'], + '{password}'=>$row['password'] + ); + } + } + } + +Now all you need to do is pass an instance of your class into the Decorator +plugin's constructor instead of passing an array. + +.. code-block:: php + + $decorator = new Swift_Plugins_DecoratorPlugin(new DbReplacements()); + + $mailer->registerPlugin($decorator); + +For each message sent, the plugin will call your class' ``getReplacementsFor()`` +method to find the array of replacements it needs. + +.. note:: + + If your lookup algorithm is case sensitive, you should transform the + ``$address`` argument as appropriate -- for example by passing it + through ``strtolower()``. diff --git a/vendor/swiftmailer/swiftmailer/doc/sending.rst b/vendor/swiftmailer/swiftmailer/doc/sending.rst new file mode 100644 index 0000000..29bf05e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/sending.rst @@ -0,0 +1,592 @@ +Sending Messages +================ + +Quick Reference for Sending a Message +------------------------------------- + +Sending a message is very straightforward. You create a Transport, use it to +create the Mailer, then you use the Mailer to send the message. + +To send a Message: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport``, ``Swift_MailTransport`` + or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Send the message via the ``send()`` method on the Mailer object. + +.. caution:: + + The ``Swift_SmtpTransport`` and ``Swift_SendmailTransport`` transports use + ``proc_*`` PHP functions, which might not be available on your PHP + installation. You can easily check if that the case by running the + following PHP script: ``setUsername('your username') + ->setPassword('your password') + ; + + /* + You could alternatively use a different transport such as Sendmail or Mail: + + // Sendmail + $transport = Swift_SendmailTransport::newInstance('/usr/sbin/sendmail -bs'); + + // Mail + $transport = Swift_MailTransport::newInstance(); + */ + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself') + ; + + // Send the message + $result = $mailer->send($message); + +Transport Types +~~~~~~~~~~~~~~~ + +A Transport is the component which actually does the sending. You need to +provide a Transport object to the Mailer class and there are several possible +options. + +Typically you will not need to know how a Transport works under-the-surface, +you will only need to know how to create an instance of one, and which one to +use for your environment. + +The SMTP Transport +.................. + +The SMTP Transport sends messages over the (standardized) Simple Message +Transfer Protocol. It can deal with encryption and authentication. + +The SMTP Transport, ``Swift_SmtpTransport`` is without doubt the most commonly +used Transport because it will work on 99% of web servers (I just made that +number up, but you get the idea). All the server needs is the ability to +connect to a remote (or even local) SMTP server on the correct port number +(usually 25). + +SMTP servers often require users to authenticate with a username and password +before any mail can be sent to other domains. This is easily achieved using +Swift Mailer with the SMTP Transport. + +SMTP is a protocol -- in other words it's a "way" of communicating a job +to be done (i.e. sending a message). The SMTP protocol is the fundamental +basis on which messages are delivered all over the internet 7 days a week, 365 +days a year. For this reason it's the most "direct" method of sending messages +you can use and it's the one that will give you the most power and feedback +(such as delivery failures) when using Swift Mailer. + +Because SMTP is generally run as a remote service (i.e. you connect to it over +the network/internet) it's extremely portable from server-to-server. You can +easily store the SMTP server address and port number in a configuration file +within your application and adjust the settings accordingly if the code is +moved or if the SMTP server is changed. + +Some SMTP servers -- Google for example -- use encryption for security reasons. +Swift Mailer supports using both SSL and TLS encryption settings. + +Using the SMTP Transport +^^^^^^^^^^^^^^^^^^^^^^^^ + +The SMTP Transport is easy to use. Most configuration options can be set with +the constructor. + +To use the SMTP Transport you need to know which SMTP server your code needs +to connect to. Ask your web host if you're not sure. Lots of people ask me who +to connect to -- I really can't answer that since it's a setting that's +extremely specific to your hosting environment. + +To use the SMTP Transport: + +* Call ``Swift_SmtpTransport::newInstance()`` with the SMTP server name and + optionally with a port number (defaults to 25). + +* Use the returned object to create the Mailer. + +A connection to the SMTP server will be established upon the first call to +``send()``. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + /* + It's also possible to use multiple method calls + + $transport = Swift_SmtpTransport::newInstance() + ->setHost('smtp.example.org') + ->setPort(25) + ; + */ + +Encrypted SMTP +^^^^^^^^^^^^^^ + +You can use SSL or TLS encryption with the SMTP Transport by specifying it as +a parameter or with a method call. + +To use encryption with the SMTP Transport: + +* Pass the encryption setting as a third parameter to + ``Swift_SmtpTransport::newInstance()``; or + +* Call the ``setEncryption()`` method on the Transport. + +A connection to the SMTP server will be established upon the first call to +``send()``. The connection will be initiated with the correct encryption +settings. + +.. note:: + + For SSL or TLS encryption to work your PHP installation must have + appropriate OpenSSL transports wrappers. You can check if "tls" and/or + "ssl" are present in your PHP installation by using the PHP function + ``stream_get_transports()`` + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 587, 'ssl'); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + /* + It's also possible to use multiple method calls + + $transport = Swift_SmtpTransport::newInstance() + ->setHost('smtp.example.org') + ->setPort(587) + ->setEncryption('ssl') + ; + */ + +SMTP with a Username and Password +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Some servers require authentication. You can provide a username and password +with ``setUsername()`` and ``setPassword()`` methods. + +To use a username and password with the SMTP Transport: + +* Create the Transport with ``Swift_SmtpTransport::newInstance()``. + +* Call the ``setUsername()`` and ``setPassword()`` methods on the Transport. + +Your username and password will be used to authenticate upon first connect +when ``send()`` are first used on the Mailer. + +If authentication fails, an Exception of type ``Swift_TransportException`` will +be thrown. + +.. note:: + + If you need to know early whether or not authentication has failed and an + Exception is going to be thrown, call the ``start()`` method on the + created Transport. + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport the call setUsername() and setPassword() + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ->setUsername('username') + ->setPassword('password') + ; + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + +The Sendmail Transport +...................... + +The Sendmail Transport sends messages by communicating with a locally +installed MTA -- such as ``sendmail``. + +The Sendmail Transport, ``Swift_SendmailTransport`` does not directly connect to +any remote services. It is designed for Linux servers that have ``sendmail`` +installed. The Transport starts a local ``sendmail`` process and sends messages +to it. Usually the ``sendmail`` process will respond quickly as it spools your +messages to disk before sending them. + +The Transport is named the Sendmail Transport for historical reasons +(``sendmail`` was the "standard" UNIX tool for sending e-mail for years). It +will send messages using other transfer agents such as Exim or Postfix despite +its name, provided they have the relevant sendmail wrappers so that they can be +started with the correct command-line flags. + +It's a common misconception that because the Sendmail Transport returns a +result very quickly it must therefore deliver messages to recipients quickly +-- this is not true. It's not slow by any means, but it's certainly not +faster than SMTP when it comes to getting messages to the intended recipients. +This is because sendmail itself sends the messages over SMTP once they have +been quickly spooled to disk. + +The Sendmail Transport has the potential to be just as smart of the SMTP +Transport when it comes to notifying Swift Mailer about which recipients were +rejected, but in reality the majority of locally installed ``sendmail`` +instances are not configured well enough to provide any useful feedback. As such +Swift Mailer may report successful deliveries where they did in fact fail before +they even left your server. + +You can run the Sendmail Transport in two different modes specified by command +line flags: + +* "``-bs``" runs in SMTP mode so theoretically it will act like the SMTP + Transport + +* "``-t``" runs in piped mode with no feedback, but theoretically faster, + though not advised + +You can think of the Sendmail Transport as a sort of asynchronous SMTP Transport +-- though if you have problems with delivery failures you should try using the +SMTP Transport instead. Swift Mailer isn't doing the work here, it's simply +passing the work to somebody else (i.e. ``sendmail``). + +Using the Sendmail Transport +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To use the Sendmail Transport you simply need to call +``Swift_SendmailTransport::newInstance()`` with the command as a parameter. + +To use the Sendmail Transport you need to know where ``sendmail`` or another MTA +exists on the server. Swift Mailer uses a default value of +``/usr/sbin/sendmail``, which should work on most systems. + +You specify the entire command as a parameter (i.e. including the command line +flags). Swift Mailer supports operational modes of "``-bs``" (default) and +"``-t``". + +.. note:: + + If you run sendmail in "``-t``" mode you will get no feedback as to whether + or not sending has succeeded. Use "``-bs``" unless you have a reason not to. + +To use the Sendmail Transport: + +* Call ``Swift_SendmailTransport::newInstance()`` with the command, including + the correct command line flags. The default is to use ``/usr/sbin/sendmail + -bs`` if this is not specified. + +* Use the returned object to create the Mailer. + +A sendmail process will be started upon the first call to ``send()``. If the +process cannot be started successfully an Exception of type +``Swift_TransportException`` will be thrown. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SendmailTransport::newInstance('/usr/sbin/exim -bs'); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + +The Mail Transport +.................. + +The Mail Transport sends messages by delegating to PHP's internal +``mail()`` function. + +In my experience -- and others' -- the ``mail()`` function is not particularly +predictable, or helpful. + +Quite notably, the ``mail()`` function behaves entirely differently between +Linux and Windows servers. On linux it uses ``sendmail``, but on Windows it uses +SMTP. + +In order for the ``mail()`` function to even work at all ``php.ini`` needs to be +configured correctly, specifying the location of sendmail or of an SMTP server. + +The problem with ``mail()`` is that it "tries" to simplify things to the point +that it actually makes things more complex due to poor interface design. The +developers of Swift Mailer have gone to a lot of effort to make the Mail +Transport work with a reasonable degree of consistency. + +Serious drawbacks when using this Transport are: + +* Unpredictable message headers + +* Lack of feedback regarding delivery failures + +* Lack of support for several plugins that require real-time delivery feedback + +It's a last resort, and we say that with a passion! + +Using the Mail Transport +^^^^^^^^^^^^^^^^^^^^^^^^ + +To use the Mail Transport you simply need to call +``Swift_MailTransport::newInstance()``. It's unlikely you'll need to configure +the Transport. + +To use the Mail Transport: + +* Call ``Swift_MailTransport::newInstance()``. + +* Use the returned object to create the Mailer. + +Messages will be sent using the ``mail()`` function. + +.. note:: + + The ``mail()`` function can take a ``$additional_parameters`` parameter. + Swift Mailer sets this to "``-f%s``" by default, where the "%s" is + substituted with the address of the sender (via a ``sprintf()``) at send + time. You may override this default by passing an argument to + ``newInstance()``. + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_MailTransport::newInstance(); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + +Available Methods for Sending Messages +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Mailer class offers two methods for sending Messages -- ``send()``. +Each behaves in a slightly different way. + +When a message is sent in Swift Mailer, the Mailer class communicates with +whichever Transport class you have chosen to use. + +Each recipient in the message should either be accepted or rejected by the +Transport. For example, if the domain name on the email address is not +reachable the SMTP Transport may reject the address because it cannot process +it. Whichever method you use -- ``send()`` -- Swift Mailer will return +an integer indicating the number of accepted recipients. + +.. note:: + + It's possible to find out which recipients were rejected -- we'll cover that + later in this chapter. + +Using the ``send()`` Method +........................... + +The ``send()`` method of the ``Swift_Mailer`` class sends a message using +exactly the same logic as your Desktop mail client would use. Just pass it a +Message and get a result. + +To send a Message with ``send()``: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport``, + ``Swift_MailTransport`` or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Send the message via the ``send()`` method on the Mailer object. + +The message will be sent just like it would be sent if you used your mail +client. An integer is returned which includes the number of successful +recipients. If none of the recipients could be sent to then zero will be +returned, which equates to a boolean ``false``. If you set two +``To:`` recipients and three ``Bcc:`` recipients in the message and all of the +recipients are delivered to successfully then the value 5 will be returned. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('localhost', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself') + ; + + // Send the message + $numSent = $mailer->send($message); + + printf("Sent %d messages\n", $numSent); + + /* Note that often that only the boolean equivalent of the + return value is of concern (zero indicates FALSE) + + if ($mailer->send($message)) + { + echo "Sent\n"; + } + else + { + echo "Failed\n"; + } + + */ + +Sending Emails in Batch +....................... + +If you want to send a separate message to each recipient so that only their +own address shows up in the ``To:`` field, follow the following recipe: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport``, + ``Swift_MailTransport`` or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Iterate over the recipients and send message via the ``send()`` method on + the Mailer object. + +Each recipient of the messages receives a different copy with only their own +email address on the ``To:`` field. + +.. note:: + + In the following example, two emails are sent. One to each of + ``receiver@domain.org`` and ``other@domain.org``. These recipients will + not be aware of each other. + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('localhost', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setBody('Here is the message itself') + ; + + // Send the message + $failedRecipients = array(); + $numSent = 0; + $to = array('receiver@domain.org', 'other@domain.org' => 'A name'); + + foreach ($to as $address => $name) + { + if (is_int($address)) { + $message->setTo($name); + } else { + $message->setTo(array($address => $name)); + } + + $numSent += $mailer->send($message, $failedRecipients); + } + + printf("Sent %d messages\n", $numSent); + +Finding out Rejected Addresses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's possible to get a list of addresses that were rejected by the Transport +by using a by-reference parameter to ``send()``. + +As Swift Mailer attempts to send the message to each address given to it, if a +recipient is rejected it will be added to the array. You can pass an existing +array, otherwise one will be created by-reference. + +Collecting the list of recipients that were rejected can be useful in +circumstances where you need to "prune" a mailing list for example when some +addresses cannot be delivered to. + +Getting Failures By-reference +............................. + +Collecting delivery failures by-reference with the ``send()`` method is as +simple as passing a variable name to the method call. + +To get failed recipients by-reference: + +* Pass a by-reference variable name to the ``send()`` method of the Mailer + class. + +If the Transport rejects any of the recipients, the culprit addresses will be +added to the array provided by-reference. + +.. note:: + + If the variable name does not yet exist, it will be initialized as an + empty array and then failures will be added to that array. If the variable + already exists it will be type-cast to an array and failures will be added + to it. + + .. code-block:: php + + $mailer = Swift_Mailer::newInstance( ... ); + + $message = Swift_Message::newInstance( ... ) + ->setFrom( ... ) + ->setTo(array( + 'receiver@bad-domain.org' => 'Receiver Name', + 'other@domain.org' => 'A name', + 'other-receiver@bad-domain.org' => 'Other Name' + )) + ->setBody( ... ) + ; + + // Pass a variable name to the send() method + if (!$mailer->send($message, $failures)) + { + echo "Failures:"; + print_r($failures); + } + + /* + Failures: + Array ( + 0 => receiver@bad-domain.org, + 1 => other-receiver@bad-domain.org + ) + */ diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle new file mode 100644 index 0000000..f895752 Binary files /dev/null and b/vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle differ diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle new file mode 100644 index 0000000..e1e33cb Binary files /dev/null and b/vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle differ diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle new file mode 100644 index 0000000..5670e2b Binary files /dev/null and b/vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle differ diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php new file mode 100644 index 0000000..bdb8d99 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php @@ -0,0 +1,69 @@ +createDependenciesFor('mime.attachment') + ); + + $this->setBody($data); + $this->setFilename($filename); + if ($contentType) + { + $this->setContentType($contentType); + } + } + + /** + * Create a new Attachment. + * @param string|Swift_OutputByteStream $data + * @param string $filename + * @param string $contentType + * @return Swift_Mime_Attachment + */ + public static function newInstance($data = null, $filename = null, + $contentType = null) + { + return new self($data, $filename, $contentType); + } + + /** + * Create a new Attachment from a filesystem path. + * @param string $path + * @param string $contentType optional + * @return Swift_Mime_Attachment + */ + public static function fromPath($path, $contentType = null) + { + return self::newInstance()->setFile( + new Swift_ByteStream_FileByteStream($path), + $contentType + ); + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php new file mode 100644 index 0000000..96a4386 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php @@ -0,0 +1,175 @@ +_filters[$key] = $filter; + } + + /** + * Remove an already present StreamFilter based on its $key. + * @param string $key + */ + public function removeFilter($key) + { + unset($this->_filters[$key]); + } + + /** + * Writes $bytes to the end of the stream. + * @param string $bytes + * @throws Swift_IoException + */ + public function write($bytes) + { + $this->_writeBuffer .= $bytes; + foreach ($this->_filters as $filter) + { + if ($filter->shouldBuffer($this->_writeBuffer)) + { + return; + } + } + $this->_doWrite($this->_writeBuffer); + return ++$this->_sequence; + } + + /** + * For any bytes that are currently buffered inside the stream, force them + * off the buffer. + * + * @throws Swift_IoException + */ + public function commit() + { + $this->_doWrite($this->_writeBuffer); + } + + /** + * Attach $is to this stream. + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + $this->_mirrors[] = $is; + } + + /** + * Remove an already bound stream. + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + foreach ($this->_mirrors as $k => $stream) + { + if ($is === $stream) + { + if ($this->_writeBuffer !== '') + { + $stream->write($this->_filter($this->_writeBuffer)); + } + unset($this->_mirrors[$k]); + } + } + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + * @throws Swift_IoException + */ + public function flushBuffers() + { + if ($this->_writeBuffer !== '') + { + $this->_doWrite($this->_writeBuffer); + } + $this->_flush(); + + foreach ($this->_mirrors as $stream) + { + $stream->flushBuffers(); + } + } + + // -- Private methods + + /** Run $bytes through all filters */ + private function _filter($bytes) + { + foreach ($this->_filters as $filter) + { + $bytes = $filter->filter($bytes); + } + return $bytes; + } + + /** Just write the bytes to the stream */ + private function _doWrite($bytes) + { + $this->_commit($this->_filter($bytes)); + + foreach ($this->_mirrors as $stream) + { + $stream->write($bytes); + } + + $this->_writeBuffer = ''; + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php new file mode 100644 index 0000000..c1cd01a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php @@ -0,0 +1,188 @@ +_array = $stack; + $this->_arraySize = count($stack); + } + elseif (is_string($stack)) + { + $this->write($stack); + } + else + { + $this->_array = array(); + } + } + + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. If less bytes exist than are requested the + * remaining bytes are given instead. If no bytes are remaining at all, boolean + * false is returned. + * @param int $length + * @return string + */ + public function read($length) + { + if ($this->_offset == $this->_arraySize) + { + return false; + } + + // Don't use array slice + $end = $length + $this->_offset; + $end = $this->_arraySize<$end + ?$this->_arraySize + :$end; + $ret = ''; + for (; $this->_offset < $end; ++$this->_offset) + { + $ret .= $this->_array[$this->_offset]; + } + return $ret; + } + + /** + * Writes $bytes to the end of the stream. + * @param string $bytes + */ + public function write($bytes) + { + $to_add = str_split($bytes); + foreach ($to_add as $value) + { + $this->_array[] = $value; + } + $this->_arraySize = count($this->_array); + + foreach ($this->_mirrors as $stream) + { + $stream->write($bytes); + } + } + + /** + * Not used. + */ + public function commit() + { + } + + /** + * Attach $is to this stream. + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + $this->_mirrors[] = $is; + } + + /** + * Remove an already bound stream. + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + foreach ($this->_mirrors as $k => $stream) + { + if ($is === $stream) + { + unset($this->_mirrors[$k]); + } + } + } + + /** + * Move the internal read pointer to $byteOffset in the stream. + * @param int $byteOffset + * @return boolean + */ + public function setReadPointer($byteOffset) + { + if ($byteOffset > $this->_arraySize) + { + $byteOffset = $this->_arraySize; + } + elseif ($byteOffset < 0) + { + $byteOffset = 0; + } + + $this->_offset = $byteOffset; + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + */ + public function flushBuffers() + { + $this->_offset = 0; + $this->_array = array(); + $this->_arraySize = 0; + + foreach ($this->_mirrors as $stream) + { + $stream->flushBuffers(); + } + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php new file mode 100644 index 0000000..45be3c2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php @@ -0,0 +1,234 @@ +_path = $path; + $this->_mode = $writable ? 'w+b' : 'rb'; + + if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) + { + $this->_quotes = true; + } + } + + /** + * Get the complete path to the file. + * @return string + */ + public function getPath() + { + return $this->_path; + } + + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. If less bytes exist than are requested the + * remaining bytes are given instead. If no bytes are remaining at all, boolean + * false is returned. + * @param int $length + * @return string + * @throws Swift_IoException + */ + public function read($length) + { + $fp = $this->_getReadHandle(); + if (!feof($fp)) + { + if ($this->_quotes) + { + ini_set('magic_quotes_runtime', 0); + } + $bytes = fread($fp, $length); + if ($this->_quotes) + { + ini_set('magic_quotes_runtime', 1); + } + $this->_offset = ftell($fp); + return $bytes; + } + else + { + $this->_resetReadHandle(); + return false; + } + } + + /** + * Move the internal read pointer to $byteOffset in the stream. + * @param int $byteOffset + * @return boolean + */ + public function setReadPointer($byteOffset) + { + if (isset($this->_reader)) + { + $this->_seekReadStreamToPosition($byteOffset); + } + $this->_offset = $byteOffset; + } + + // -- Private methods + + /** Just write the bytes to the file */ + protected function _commit($bytes) + { + fwrite($this->_getWriteHandle(), $bytes); + $this->_resetReadHandle(); + } + + /** Not used */ + protected function _flush() + { + } + + /** Get the resource for reading */ + private function _getReadHandle() + { + if (!isset($this->_reader)) + { + if (!$this->_reader = fopen($this->_path, 'rb')) + { + throw new Swift_IoException( + 'Unable to open file for reading [' . $this->_path . ']' + ); + } + if ($this->_offset <> 0) + { + $this->_getReadStreamSeekableStatus(); + $this->_seekReadStreamToPosition($this->_offset); + } + } + return $this->_reader; + } + + /** Get the resource for writing */ + private function _getWriteHandle() + { + if (!isset($this->_writer)) + { + if (!$this->_writer = fopen($this->_path, $this->_mode)) + { + throw new Swift_IoException( + 'Unable to open file for writing [' . $this->_path . ']' + ); + } + } + return $this->_writer; + } + + /** Force a reload of the resource for reading */ + private function _resetReadHandle() + { + if (isset($this->_reader)) + { + fclose($this->_reader); + $this->_reader = null; + } + } + + /** Check if ReadOnly Stream is seekable */ + private function _getReadStreamSeekableStatus() + { + $metas = stream_get_meta_data($this->_reader); + $this->_seekable = $metas['seekable']; + } + + /** Streams in a readOnly stream ensuring copy if needed */ + private function _seekReadStreamToPosition($offset) + { + if ($this->_seekable===null) + { + $this->_getReadStreamSeekableStatus(); + } + if ($this->_seekable === false) + { + $currentPos = ftell($this->_reader); + if ($currentPos<$offset) + { + $toDiscard = $offset-$currentPos; + fread($this->_reader, $toDiscard); + return; + } + $this->_copyReadStream(); + } + fseek($this->_reader, $offset, SEEK_SET); + } + + /** Copy a readOnly Stream to ensure seekability */ + private function _copyReadStream() + { + if ($tmpFile = fopen('php://temp/maxmemory:4096', 'w+b')) + { + /* We have opened a php:// Stream Should work without problem */ + } + elseif (function_exists('sys_get_temp_dir') && is_writable(sys_get_temp_dir()) && ($tmpFile = tmpfile())) + { + /* We have opened a tmpfile */ + } + else + { + throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available'); + } + $currentPos = ftell($this->_reader); + fclose($this->_reader); + $source = fopen($this->_path, 'rb'); + if (!$source) + { + throw new Swift_IoException('Unable to open file for copying [' . $this->_path . ']'); + } + fseek($tmpFile, 0, SEEK_SET); + while (!feof($source)) + { + fwrite($tmpFile, fread($source, 4096)); + } + fseek($tmpFile, $currentPos, SEEK_SET); + fclose($source); + $this->_reader = $tmpFile; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php new file mode 100644 index 0000000..c660a88 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php @@ -0,0 +1,60 @@ + + */ +interface Swift_CharacterReader +{ + const MAP_TYPE_INVALID = 0x01; + const MAP_TYPE_FIXED_LEN = 0x02; + const MAP_TYPE_POSITIONS = 0x03; + + /** + * Returns the complete charactermap + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * @return int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars); + + /** + * Returns mapType + * @return int mapType + */ + public function getMapType(); + + /** + * Returns an integer which specifies how many more bytes to read. + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * @param int[] $bytes + * @return int + */ + public function validateByteSequence($bytes, $size); + + /** + * Returns the number of bytes which should be read to start each character. + * For fixed width character sets this should be the number of + * octets-per-character. For multibyte character sets this will probably be 1. + * @return int + */ + public function getInitialByteSize(); + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php new file mode 100644 index 0000000..8345ac5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php @@ -0,0 +1,91 @@ + + */ +class Swift_CharacterReader_GenericFixedWidthReader + implements Swift_CharacterReader +{ + /** + * The number of bytes in a single character. + * @var int + * @access private + */ + private $_width; + + /** + * Creates a new GenericFixedWidthReader using $width bytes per character. + * @param int $width + */ + public function __construct($width) + { + $this->_width = $width; + } + + /** + * Returns the complete charactermap + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * @return $int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars) + { + $strlen = strlen($string); + // % and / are CPU intensive, so, maybe find a better way + $ignored = $strlen%$this->_width; + $ignoredChars = substr($string, - $ignored); + $currentMap = $this->_width; + + return ($strlen - $ignored)/$this->_width; + } + + /** + * Returns mapType + * @return int mapType + */ + public function getMapType() + { + return self::MAP_TYPE_FIXED_LEN; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * @param string $bytes + * @return int + */ + public function validateByteSequence($bytes, $size) + { + $needed = $this->_width - $size; + + return ($needed > -1) ? $needed : -1; + } + + /** + * Returns the number of bytes which should be read to start each character. + * @return int + */ + public function getInitialByteSize() + { + return $this->_width; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php new file mode 100644 index 0000000..7903949 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php @@ -0,0 +1,82 @@ +"\x07F") + { // Invalid char + $currentMap[$i+$startOffset]=$string[$i]; + } + } + return $strlen; + } + + /** + * Returns mapType + * @return int mapType + */ + public function getMapType() + { + return self::MAP_TYPE_INVALID; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * @param string $bytes + * @return int + */ + public function validateByteSequence($bytes, $size) + { + $byte = reset($bytes); + if (1 == count($bytes) && $byte >= 0x00 && $byte <= 0x7F) + { + return 0; + } + else + { + return -1; + } + } + + /** + * Returns the number of bytes which should be read to start each character. + * @return int + */ + public function getInitialByteSize() + { + return 1; + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php new file mode 100644 index 0000000..deea4b3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php @@ -0,0 +1,182 @@ + + */ +class Swift_CharacterReader_Utf8Reader + implements Swift_CharacterReader +{ + + /** Pre-computed for optimization */ + private static $length_map=array( +//N=0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, //0x0N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, //0x1N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, //0x2N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, //0x3N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, //0x4N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, //0x5N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, //0x6N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, //0x7N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //0x8N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //0x9N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //0xAN + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //0xBN + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, //0xCN + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, //0xDN + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, //0xEN + 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0 //0xFN + ); + private static $s_length_map=array( + "\x00"=>1, "\x01"=>1, "\x02"=>1, "\x03"=>1, "\x04"=>1, "\x05"=>1, "\x06"=>1, "\x07"=>1, + "\x08"=>1, "\x09"=>1, "\x0a"=>1, "\x0b"=>1, "\x0c"=>1, "\x0d"=>1, "\x0e"=>1, "\x0f"=>1, + "\x10"=>1, "\x11"=>1, "\x12"=>1, "\x13"=>1, "\x14"=>1, "\x15"=>1, "\x16"=>1, "\x17"=>1, + "\x18"=>1, "\x19"=>1, "\x1a"=>1, "\x1b"=>1, "\x1c"=>1, "\x1d"=>1, "\x1e"=>1, "\x1f"=>1, + "\x20"=>1, "\x21"=>1, "\x22"=>1, "\x23"=>1, "\x24"=>1, "\x25"=>1, "\x26"=>1, "\x27"=>1, + "\x28"=>1, "\x29"=>1, "\x2a"=>1, "\x2b"=>1, "\x2c"=>1, "\x2d"=>1, "\x2e"=>1, "\x2f"=>1, + "\x30"=>1, "\x31"=>1, "\x32"=>1, "\x33"=>1, "\x34"=>1, "\x35"=>1, "\x36"=>1, "\x37"=>1, + "\x38"=>1, "\x39"=>1, "\x3a"=>1, "\x3b"=>1, "\x3c"=>1, "\x3d"=>1, "\x3e"=>1, "\x3f"=>1, + "\x40"=>1, "\x41"=>1, "\x42"=>1, "\x43"=>1, "\x44"=>1, "\x45"=>1, "\x46"=>1, "\x47"=>1, + "\x48"=>1, "\x49"=>1, "\x4a"=>1, "\x4b"=>1, "\x4c"=>1, "\x4d"=>1, "\x4e"=>1, "\x4f"=>1, + "\x50"=>1, "\x51"=>1, "\x52"=>1, "\x53"=>1, "\x54"=>1, "\x55"=>1, "\x56"=>1, "\x57"=>1, + "\x58"=>1, "\x59"=>1, "\x5a"=>1, "\x5b"=>1, "\x5c"=>1, "\x5d"=>1, "\x5e"=>1, "\x5f"=>1, + "\x60"=>1, "\x61"=>1, "\x62"=>1, "\x63"=>1, "\x64"=>1, "\x65"=>1, "\x66"=>1, "\x67"=>1, + "\x68"=>1, "\x69"=>1, "\x6a"=>1, "\x6b"=>1, "\x6c"=>1, "\x6d"=>1, "\x6e"=>1, "\x6f"=>1, + "\x70"=>1, "\x71"=>1, "\x72"=>1, "\x73"=>1, "\x74"=>1, "\x75"=>1, "\x76"=>1, "\x77"=>1, + "\x78"=>1, "\x79"=>1, "\x7a"=>1, "\x7b"=>1, "\x7c"=>1, "\x7d"=>1, "\x7e"=>1, "\x7f"=>1, + "\x80"=>0, "\x81"=>0, "\x82"=>0, "\x83"=>0, "\x84"=>0, "\x85"=>0, "\x86"=>0, "\x87"=>0, + "\x88"=>0, "\x89"=>0, "\x8a"=>0, "\x8b"=>0, "\x8c"=>0, "\x8d"=>0, "\x8e"=>0, "\x8f"=>0, + "\x90"=>0, "\x91"=>0, "\x92"=>0, "\x93"=>0, "\x94"=>0, "\x95"=>0, "\x96"=>0, "\x97"=>0, + "\x98"=>0, "\x99"=>0, "\x9a"=>0, "\x9b"=>0, "\x9c"=>0, "\x9d"=>0, "\x9e"=>0, "\x9f"=>0, + "\xa0"=>0, "\xa1"=>0, "\xa2"=>0, "\xa3"=>0, "\xa4"=>0, "\xa5"=>0, "\xa6"=>0, "\xa7"=>0, + "\xa8"=>0, "\xa9"=>0, "\xaa"=>0, "\xab"=>0, "\xac"=>0, "\xad"=>0, "\xae"=>0, "\xaf"=>0, + "\xb0"=>0, "\xb1"=>0, "\xb2"=>0, "\xb3"=>0, "\xb4"=>0, "\xb5"=>0, "\xb6"=>0, "\xb7"=>0, + "\xb8"=>0, "\xb9"=>0, "\xba"=>0, "\xbb"=>0, "\xbc"=>0, "\xbd"=>0, "\xbe"=>0, "\xbf"=>0, + "\xc0"=>2, "\xc1"=>2, "\xc2"=>2, "\xc3"=>2, "\xc4"=>2, "\xc5"=>2, "\xc6"=>2, "\xc7"=>2, + "\xc8"=>2, "\xc9"=>2, "\xca"=>2, "\xcb"=>2, "\xcc"=>2, "\xcd"=>2, "\xce"=>2, "\xcf"=>2, + "\xd0"=>2, "\xd1"=>2, "\xd2"=>2, "\xd3"=>2, "\xd4"=>2, "\xd5"=>2, "\xd6"=>2, "\xd7"=>2, + "\xd8"=>2, "\xd9"=>2, "\xda"=>2, "\xdb"=>2, "\xdc"=>2, "\xdd"=>2, "\xde"=>2, "\xdf"=>2, + "\xe0"=>3, "\xe1"=>3, "\xe2"=>3, "\xe3"=>3, "\xe4"=>3, "\xe5"=>3, "\xe6"=>3, "\xe7"=>3, + "\xe8"=>3, "\xe9"=>3, "\xea"=>3, "\xeb"=>3, "\xec"=>3, "\xed"=>3, "\xee"=>3, "\xef"=>3, + "\xf0"=>4, "\xf1"=>4, "\xf2"=>4, "\xf3"=>4, "\xf4"=>4, "\xf5"=>4, "\xf6"=>4, "\xf7"=>4, + "\xf8"=>5, "\xf9"=>5, "\xfa"=>5, "\xfb"=>5, "\xfc"=>6, "\xfd"=>6, "\xfe"=>0, "\xff"=>0, + ); + + /** + * Returns the complete charactermap + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars) + { + if (!isset($currentMap['i']) || !isset($currentMap['p'])) + { + $currentMap['p'] = $currentMap['i'] = array(); + } + $strlen=strlen($string); + $charPos=count($currentMap['p']); + $foundChars=0; + $invalid=false; + for ($i=0; $i<$strlen; ++$i) + { + $char=$string[$i]; + $size=self::$s_length_map[$char]; + if ($size==0) + { + /* char is invalid, we must wait for a resync */ + $invalid=true; + continue; + } + else + { + if ($invalid==true) + { + /* We mark the chars as invalid and start a new char */ + $currentMap['p'][$charPos+$foundChars]=$startOffset+$i; + $currentMap['i'][$charPos+$foundChars]=true; + ++$foundChars; + $invalid=false; + } + if (($i+$size) > $strlen){ + $ignoredChars=substr($string, $i); + break; + } + for ($j=1; $j<$size; ++$j) + { + $char=$string[$i+$j]; + if ($char>"\x7F" && $char<"\xC0") + { + // Valid - continue parsing + } + else + { + /* char is invalid, we must wait for a resync */ + $invalid=true; + continue 2; + } + } + /* Ok we got a complete char here */ + $currentMap['p'][$charPos+$foundChars]=$startOffset+$i+$size; + $i+=$j-1; + ++$foundChars; + } + } + return $foundChars; + } + + /** + * Returns mapType + * @return int mapType + */ + public function getMapType() + { + return self::MAP_TYPE_POSITIONS; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * @param string $bytes + * @return int + */ + public function validateByteSequence($bytes, $size) + { + if ($size<1){ + return -1; + } + $needed = self::$length_map[$bytes[0]] - $size; + return ($needed > -1) + ? $needed + : -1 + ; + } + + /** + * Returns the number of bytes which should be read to start each character. + * @return int + */ + public function getInitialByteSize() + { + return 1; + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php new file mode 100644 index 0000000..55ce880 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php @@ -0,0 +1,28 @@ +init(); + } + + public function __wakeup() + { + $this->init(); + } + + public function init() + { + if(count(self::$_map) > 0) + { + return; + } + + $prefix = 'Swift_CharacterReader_'; + + $singleByte = array( + 'class' => $prefix . 'GenericFixedWidthReader', + 'constructor' => array(1) + ); + + $doubleByte = array( + 'class' => $prefix . 'GenericFixedWidthReader', + 'constructor' => array(2) + ); + + $fourBytes = array( + 'class' => $prefix . 'GenericFixedWidthReader', + 'constructor' => array(4) + ); + + //Utf-8 + self::$_map['utf-?8'] = array( + 'class' => $prefix . 'Utf8Reader', + 'constructor' => array() + ); + + //7-8 bit charsets + self::$_map['(us-)?ascii'] = $singleByte; + self::$_map['(iso|iec)-?8859-?[0-9]+'] = $singleByte; + self::$_map['windows-?125[0-9]'] = $singleByte; + self::$_map['cp-?[0-9]+'] = $singleByte; + self::$_map['ansi'] = $singleByte; + self::$_map['macintosh'] = $singleByte; + self::$_map['koi-?7'] = $singleByte; + self::$_map['koi-?8-?.+'] = $singleByte; + self::$_map['mik'] = $singleByte; + self::$_map['(cork|t1)'] = $singleByte; + self::$_map['v?iscii'] = $singleByte; + + //16 bits + self::$_map['(ucs-?2|utf-?16)'] = $doubleByte; + + //32 bits + self::$_map['(ucs-?4|utf-?32)'] = $fourBytes; + + //Fallback + self::$_map['.*'] = $singleByte; + } + + /** + * Returns a CharacterReader suitable for the charset applied. + * @param string $charset + * @return Swift_CharacterReader + */ + public function getReaderFor($charset) + { + $charset = trim(strtolower($charset)); + foreach (self::$_map as $pattern => $spec) + { + $re = '/^' . $pattern . '$/D'; + if (preg_match($re, $charset)) + { + if (!array_key_exists($pattern, self::$_loaded)) + { + $reflector = new ReflectionClass($spec['class']); + if ($reflector->getConstructor()) + { + $reader = $reflector->newInstanceArgs($spec['constructor']); + } + else + { + $reader = $reflector->newInstance(); + } + self::$_loaded[$pattern] = $reader; + } + return self::$_loaded[$pattern]; + } + } + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php new file mode 100644 index 0000000..bf91528 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php @@ -0,0 +1,86 @@ +setCharacterReaderFactory($factory); + $this->setCharacterSet($charset); + } + + /** + * Set the character set used in this CharacterStream. + * @param string $charset + */ + public function setCharacterSet($charset) + { + $this->_charset = $charset; + $this->_charReader = null; + } + + /** + * Set the CharacterReaderFactory for multi charset support. + * @param Swift_CharacterReaderFactory $factory + */ + public function setCharacterReaderFactory( + Swift_CharacterReaderFactory $factory) + { + $this->_charReaderFactory = $factory; + } + + /** + * Overwrite this character stream using the byte sequence in the byte stream. + * @param Swift_OutputByteStream $os output stream to read from + */ + public function importByteStream(Swift_OutputByteStream $os) + { + if (!isset($this->_charReader)) + { + $this->_charReader = $this->_charReaderFactory + ->getReaderFor($this->_charset); + } + + $startLength = $this->_charReader->getInitialByteSize(); + while (false !== $bytes = $os->read($startLength)) + { + $c = array(); + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) + { + $c[] = self::$_byteMap[$bytes[$i]]; + } + $size = count($c); + $need = $this->_charReader + ->validateByteSequence($c, $size); + if ($need > 0 && + false !== $bytes = $os->read($need)) + { + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) + { + $c[] = self::$_byteMap[$bytes[$i]]; + } + } + $this->_array[] = $c; + ++$this->_array_size; + } + } + + /** + * Import a string a bytes into this CharacterStream, overwriting any existing + * data in the stream. + * @param string $string + */ + public function importString($string) + { + $this->flushContents(); + $this->write($string); + } + + /** + * Read $length characters from the stream and move the internal pointer + * $length further into the stream. + * @param int $length + * @return string + */ + public function read($length) + { + if ($this->_offset == $this->_array_size) + { + return false; + } + + // Don't use array slice + $arrays = array(); + $end = $length + $this->_offset; + for ($i = $this->_offset; $i < $end; ++$i) + { + if (!isset($this->_array[$i])) + { + break; + } + $arrays[] = $this->_array[$i]; + } + $this->_offset += $i - $this->_offset; // Limit function calls + $chars = false; + foreach ($arrays as $array) + { + $chars .= implode('', array_map('chr', $array)); + } + return $chars; + } + + /** + * Read $length characters from the stream and return a 1-dimensional array + * containing there octet values. + * @param int $length + * @return int[] + */ + public function readBytes($length) + { + if ($this->_offset == $this->_array_size) + { + return false; + } + $arrays = array(); + $end = $length + $this->_offset; + for ($i = $this->_offset; $i < $end; ++$i) + { + if (!isset($this->_array[$i])) + { + break; + } + $arrays[] = $this->_array[$i]; + } + $this->_offset += ($i - $this->_offset); // Limit function calls + return call_user_func_array('array_merge', $arrays); + } + + /** + * Write $chars to the end of the stream. + * @param string $chars + */ + public function write($chars) + { + if (!isset($this->_charReader)) + { + $this->_charReader = $this->_charReaderFactory->getReaderFor( + $this->_charset); + } + + $startLength = $this->_charReader->getInitialByteSize(); + + $fp = fopen('php://memory', 'w+b'); + fwrite($fp, $chars); + unset($chars); + fseek($fp, 0, SEEK_SET); + + $buffer = array(0); + $buf_pos = 1; + $buf_len = 1; + $has_datas = true; + do + { + $bytes = array(); + // Buffer Filing + if ($buf_len - $buf_pos < $startLength) + { + $buf = array_splice($buffer, $buf_pos); + $new = $this->_reloadBuffer($fp, 100); + if ($new) + { + $buffer = array_merge($buf, $new); + $buf_len = count($buffer); + $buf_pos = 0; + } + else + { + $has_datas = false; + } + } + if ($buf_len - $buf_pos > 0) + { + $size = 0; + for ($i = 0; $i < $startLength && isset($buffer[$buf_pos]); ++$i) + { + ++$size; + $bytes[] = $buffer[$buf_pos++]; + } + $need = $this->_charReader->validateByteSequence( + $bytes, $size); + if ($need > 0) + { + if ($buf_len - $buf_pos < $need) + { + $new = $this->_reloadBuffer($fp, $need); + + if ($new) + { + $buffer = array_merge($buffer, $new); + $buf_len = count($buffer); + } + } + for ($i = 0; $i < $need && isset($buffer[$buf_pos]); ++$i) + { + $bytes[] = $buffer[$buf_pos++]; + } + } + $this->_array[] = $bytes; + ++$this->_array_size; + } + } + while ($has_datas); + + fclose($fp); + } + + /** + * Move the internal pointer to $charOffset in the stream. + * @param int $charOffset + */ + public function setPointer($charOffset) + { + if ($charOffset > $this->_array_size) + { + $charOffset = $this->_array_size; + } + elseif ($charOffset < 0) + { + $charOffset = 0; + } + $this->_offset = $charOffset; + } + + /** + * Empty the stream and reset the internal pointer. + */ + public function flushContents() + { + $this->_offset = 0; + $this->_array = array(); + $this->_array_size = 0; + } + + private function _reloadBuffer($fp, $len) + { + if (!feof($fp) && ($bytes = fread($fp, $len)) !== false) + { + $buf = array(); + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) + { + $buf[] = self::$_byteMap[$bytes[$i]]; + } + return $buf; + } + return false; + } + + private static function _initializeMaps() + { + if (!isset(self::$_charMap)) + { + self::$_charMap = array(); + for ($byte = 0; $byte < 256; ++$byte) + { + self::$_charMap[$byte] = chr($byte); + } + self::$_byteMap = array_flip(self::$_charMap); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php new file mode 100644 index 0000000..57c3fb7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php @@ -0,0 +1,298 @@ +. + + */ + + + +/** + * A CharacterStream implementation which stores characters in an internal array. + * @package Swift + * @subpackage CharacterStream + * @author Xavier De Cock + */ + +Class Swift_CharacterStream_NgCharacterStream + implements Swift_CharacterStream +{ + + /** + * The char reader (lazy-loaded) for the current charset. + * @var Swift_CharacterReader + * @access private + */ + private $_charReader; + + /** + * A factory for creatiing CharacterReader instances. + * @var Swift_CharacterReaderFactory + * @access private + */ + private $_charReaderFactory; + + /** + * The character set this stream is using. + * @var string + * @access private + */ + private $_charset; + + /** + * The datas stored as is + * + * @var string + */ + private $_datas = ""; + + /** + * Number of bytes in the stream + * + * @var int + */ + private $_datasSize = 0; + + /** + * Map + * + * @var mixed + */ + private $_map; + + /** + * Map Type + * + * @var int + */ + private $_mapType = 0; + + /** + * Number of characters in the stream + * + * @var int + */ + private $_charCount = 0; + + /** + * Position in the stream + * + * @var unknown_type + */ + private $_currentPos = 0; + + /** + * The constructor + * + * @param Swift_CharacterReaderFactory $factory + * @param unknown_type $charset + */ + public function __construct(Swift_CharacterReaderFactory $factory, + $charset) + { + $this->setCharacterReaderFactory($factory); + $this->setCharacterSet($charset); + } + + /* -- Changing parameters of the stream -- */ + + /** + * Set the character set used in this CharacterStream. + * @param string $charset + */ + public function setCharacterSet($charset) + { + $this->_charset = $charset; + $this->_charReader = null; + $this->_mapType = 0; + } + + /** + * Set the CharacterReaderFactory for multi charset support. + * @param Swift_CharacterReaderFactory $factory + */ + public function setCharacterReaderFactory( + Swift_CharacterReaderFactory $factory) + { + $this->_charReaderFactory = $factory; + } + + /** + * @see Swift_CharacterStream::flushContents() + * + */ + public function flushContents() + { + $this->_datas = null; + $this->_map = null; + $this->_charCount = 0; + $this->_currentPos = 0; + $this->_datasSize = 0; + } + + /** + * @see Swift_CharacterStream::importByteStream() + * + * @param Swift_OutputByteStream $os + */ + public function importByteStream(Swift_OutputByteStream $os) + { + $this->flushContents(); + $blocks=512; + $os->setReadPointer(0); + while(false!==($read = $os->read($blocks))) + $this->write($read); + } + + /** + * @see Swift_CharacterStream::importString() + * + * @param string $string + */ + public function importString($string) + { + $this->flushContents(); + $this->write($string); + } + + /** + * @see Swift_CharacterStream::read() + * + * @param int $length + * @return string + */ + public function read($length) + { + if ($this->_currentPos>=$this->_charCount) + { + return false; + } + $ret=false; + $length = ($this->_currentPos+$length > $this->_charCount) + ? $this->_charCount - $this->_currentPos + : $length; + switch ($this->_mapType) + { + case Swift_CharacterReader::MAP_TYPE_FIXED_LEN: + $len = $length*$this->_map; + $ret = substr($this->_datas, + $this->_currentPos * $this->_map, + $len); + $this->_currentPos += $length; + break; + + case Swift_CharacterReader::MAP_TYPE_INVALID: + $end = $this->_currentPos + $length; + $end = $end > $this->_charCount + ?$this->_charCount + :$end; + $ret = ''; + for (; $this->_currentPos < $length; ++$this->_currentPos) + { + if (isset ($this->_map[$this->_currentPos])) + { + $ret .= '?'; + } + else + { + $ret .= $this->_datas[$this->_currentPos]; + } + } + break; + + case Swift_CharacterReader::MAP_TYPE_POSITIONS: + $end = $this->_currentPos + $length; + $end = $end > $this->_charCount + ?$this->_charCount + :$end; + $ret = ''; + $start = 0; + if ($this->_currentPos>0) + { + $start = $this->_map['p'][$this->_currentPos-1]; + } + $to = $start; + for (; $this->_currentPos < $end; ++$this->_currentPos) + { + if (isset($this->_map['i'][$this->_currentPos])) { + $ret .= substr($this->_datas, $start, $to - $start).'?'; + $start = $this->_map['p'][$this->_currentPos]; + } else { + $to = $this->_map['p'][$this->_currentPos]; + } + } + $ret .= substr($this->_datas, $start, $to - $start); + break; + } + return $ret; + } + + /** + * @see Swift_CharacterStream::readBytes() + * + * @param int $length + * @return int[] + */ + public function readBytes($length) + { + $read=$this->read($length); + if ($read!==false) + { + $ret = array_map('ord', str_split($read, 1)); + return $ret; + } + return false; + } + + /** + * @see Swift_CharacterStream::setPointer() + * + * @param int $charOffset + */ + public function setPointer($charOffset) + { + if ($this->_charCount<$charOffset){ + $charOffset=$this->_charCount; + } + $this->_currentPos = $charOffset; + } + + /** + * @see Swift_CharacterStream::write() + * + * @param string $chars + */ + public function write($chars) + { + if (!isset($this->_charReader)) + { + $this->_charReader = $this->_charReaderFactory->getReaderFor( + $this->_charset); + $this->_map = array(); + $this->_mapType = $this->_charReader->getMapType(); + } + $ignored=''; + $this->_datas .= $chars; + $this->_charCount += $this->_charReader->getCharPositions(substr($this->_datas, $this->_datasSize), $this->_datasSize, $this->_map, $ignored); + if ($ignored!==false) { + $this->_datasSize=strlen($this->_datas)-strlen($ignored); + } + else + { + $this->_datasSize=strlen($this->_datas); + } + } +} \ No newline at end of file diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php new file mode 100644 index 0000000..27a9b6c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Base class for Spools (implements time and message limits). + * @package Swift + * @author Fabien Potencier + */ +abstract class Swift_ConfigurableSpool implements Swift_Spool +{ + /** The maximum number of messages to send per flush */ + private $_message_limit; + + /** The time limit per flush */ + private $_time_limit; + + /** + * Sets the maximum number of messages to send per flush. + * @param int $limit The limit + */ + public function setMessageLimit($limit) + { + $this->_message_limit = (int) $limit; + } + + /** + * Gets the maximum number of messages to send per flush. + * @return int The limit + */ + public function getMessageLimit() + { + return $this->_message_limit; + } + + /** + * Sets the time limit (in seconds) per flush. + * @param int $limit The limit + */ + public function setTimeLimit($limit) + { + $this->_time_limit = (int) $limit; + } + + /** + * Gets the time limit (in seconds) per flush. + * @return int The limit + */ + public function getTimeLimit() + { + return $this->_time_limit; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php new file mode 100644 index 0000000..066811e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php @@ -0,0 +1,348 @@ +_store); + } + + /** + * Test if an item is registered in this container with the given name. + * @param string $itemName + * @return boolean + * @see register() + */ + public function has($itemName) + { + return array_key_exists($itemName, $this->_store) + && isset($this->_store[$itemName]['lookupType']); + } + + /** + * Lookup the item with the given $itemName. + * @param string $itemName + * @return mixed + * @throws Swift_DependencyException If the dependency is not found + * @see register() + */ + public function lookup($itemName) + { + if (!$this->has($itemName)) + { + throw new Swift_DependencyException( + 'Cannot lookup dependency "' . $itemName . '" since it is not registered.' + ); + } + + switch ($this->_store[$itemName]['lookupType']) + { + case self::TYPE_ALIAS: + return $this->_createAlias($itemName); + case self::TYPE_VALUE: + return $this->_getValue($itemName); + case self::TYPE_INSTANCE: + return $this->_createNewInstance($itemName); + case self::TYPE_SHARED: + return $this->_createSharedInstance($itemName); + } + } + + /** + * Create an array of arguments passed to the constructor of $itemName. + * @param string $itemName + * @return array + */ + public function createDependenciesFor($itemName) + { + $args = array(); + if (isset($this->_store[$itemName]['args'])) + { + $args = $this->_resolveArgs($this->_store[$itemName]['args']); + } + return $args; + } + + /** + * Register a new dependency with $itemName. + * This method returns the current DependencyContainer instance because it + * requires the use of the fluid interface to set the specific details for the + * dependency. + * + * @param string $itemName + * @return Swift_DependencyContainer + * @see asNewInstanceOf(), asSharedInstanceOf(), asValue() + */ + public function register($itemName) + { + $this->_store[$itemName] = array(); + $this->_endPoint =& $this->_store[$itemName]; + return $this; + } + + /** + * Specify the previously registered item as a literal value. + * {@link register()} must be called before this will work. + * + * @param mixed $value + * @return Swift_DependencyContainer + */ + public function asValue($value) + { + $endPoint =& $this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_VALUE; + $endPoint['value'] = $value; + return $this; + } + + /** + * Specify the previously registered item as an alias of another item. + * @param string $lookup + * @return Swift_DependencyContainer + */ + public function asAliasOf($lookup) + { + $endPoint =& $this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_ALIAS; + $endPoint['ref'] = $lookup; + return $this; + } + + /** + * Specify the previously registered item as a new instance of $className. + * {@link register()} must be called before this will work. + * Any arguments can be set with {@link withDependencies()}, + * {@link addConstructorValue()} or {@link addConstructorLookup()}. + * + * @param string $className + * @return Swift_DependencyContainer + * @see withDependencies(), addConstructorValue(), addConstructorLookup() + */ + public function asNewInstanceOf($className) + { + $endPoint =& $this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_INSTANCE; + $endPoint['className'] = $className; + return $this; + } + + /** + * Specify the previously registered item as a shared instance of $className. + * {@link register()} must be called before this will work. + * @param string $className + * @return Swift_DependencyContainer + */ + public function asSharedInstanceOf($className) + { + $endPoint =& $this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_SHARED; + $endPoint['className'] = $className; + return $this; + } + + /** + * Specify a list of injected dependencies for the previously registered item. + * This method takes an array of lookup names. + * + * @param array $lookups + * @return Swift_DependencyContainer + * @see addConstructorValue(), addConstructorLookup() + */ + public function withDependencies(array $lookups) + { + $endPoint =& $this->_getEndPoint(); + $endPoint['args'] = array(); + foreach ($lookups as $lookup) + { + $this->addConstructorLookup($lookup); + } + return $this; + } + + /** + * Specify a literal (non looked up) value for the constructor of the + * previously registered item. + * + * @param mixed $value + * @return Swift_DependencyContainer + * @see withDependencies(), addConstructorLookup() + */ + public function addConstructorValue($value) + { + $endPoint =& $this->_getEndPoint(); + if (!isset($endPoint['args'])) + { + $endPoint['args'] = array(); + } + $endPoint['args'][] = array('type' => 'value', 'item' => $value); + return $this; + } + + /** + * Specify a dependency lookup for the constructor of the previously + * registered item. + * + * @param string $lookup + * @return Swift_DependencyContainer + * @see withDependencies(), addConstructorValue() + */ + public function addConstructorLookup($lookup) + { + $endPoint =& $this->_getEndPoint(); + if (!isset($this->_endPoint['args'])) + { + $endPoint['args'] = array(); + } + $endPoint['args'][] = array('type' => 'lookup', 'item' => $lookup); + return $this; + } + + // -- Private methods + + /** Get the literal value with $itemName */ + private function _getValue($itemName) + { + return $this->_store[$itemName]['value']; + } + + /** Resolve an alias to another item */ + private function _createAlias($itemName) + { + return $this->lookup($this->_store[$itemName]['ref']); + } + + /** Create a fresh instance of $itemName */ + private function _createNewInstance($itemName) + { + $reflector = new ReflectionClass($this->_store[$itemName]['className']); + if ($reflector->getConstructor()) + { + return $reflector->newInstanceArgs( + $this->createDependenciesFor($itemName) + ); + } + else + { + return $reflector->newInstance(); + } + } + + /** Create and register a shared instance of $itemName */ + private function _createSharedInstance($itemName) + { + if (!isset($this->_store[$itemName]['instance'])) + { + $this->_store[$itemName]['instance'] = $this->_createNewInstance($itemName); + } + return $this->_store[$itemName]['instance']; + } + + /** Get the current endpoint in the store */ + private function &_getEndPoint() + { + if (!isset($this->_endPoint)) + { + throw new BadMethodCallException( + 'Component must first be registered by calling register()' + ); + } + return $this->_endPoint; + } + + /** Get an argument list with dependencies resolved */ + private function _resolveArgs(array $args) + { + $resolved = array(); + foreach ($args as $argDefinition) + { + switch ($argDefinition['type']) + { + case 'lookup': + $resolved[] = $this->_lookupRecursive($argDefinition['item']); + break; + case 'value': + $resolved[] = $argDefinition['item']; + break; + } + } + return $resolved; + } + + /** Resolve a single dependency with an collections */ + private function _lookupRecursive($item) + { + if (is_array($item)) + { + $collection = array(); + foreach ($item as $k => $v) + { + $collection[$k] = $this->_lookupRecursive($v); + } + return $collection; + } + else + { + return $this->lookup($item); + } + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php new file mode 100644 index 0000000..7622aac --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php @@ -0,0 +1,29 @@ +createDependenciesFor('mime.embeddedfile') + ); + + $this->setBody($data); + $this->setFilename($filename); + if ($contentType) + { + $this->setContentType($contentType); + } + } + + /** + * Create a new EmbeddedFile. + * @param string|Swift_OutputByteStream $data + * @param string $filename + * @param string $contentType + * @return Swift_Mime_EmbeddedFile + */ + public static function newInstance($data = null, $filename = null, + $contentType = null) + { + return new self($data, $filename, $contentType); + } + + /** + * Create a new EmbeddedFile from a filesystem path. + * @param string $path + * @return Swift_Mime_EmbeddedFile + */ + public static function fromPath($path) + { + return self::newInstance()->setFile( + new Swift_ByteStream_FileByteStream($path) + ); + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php new file mode 100644 index 0000000..3db403f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php @@ -0,0 +1,31 @@ += $maxLineLength || 76 < $maxLineLength) + { + $maxLineLength = 76; + } + + $encodedString = base64_encode($string); + $firstLine = ''; + + if (0 != $firstLineOffset) + { + $firstLine = substr( + $encodedString, 0, $maxLineLength - $firstLineOffset + ) . "\r\n"; + $encodedString = substr( + $encodedString, $maxLineLength - $firstLineOffset + ); + } + + return $firstLine . trim(chunk_split($encodedString, $maxLineLength, "\r\n")); + } + + /** + * Does nothing. + */ + public function charsetChanged($charset) + { + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php new file mode 100644 index 0000000..6efd728 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php @@ -0,0 +1,296 @@ + '=00', 1 => '=01', 2 => '=02', 3 => '=03', 4 => '=04', + 5 => '=05', 6 => '=06', 7 => '=07', 8 => '=08', 9 => '=09', + 10 => '=0A', 11 => '=0B', 12 => '=0C', 13 => '=0D', 14 => '=0E', + 15 => '=0F', 16 => '=10', 17 => '=11', 18 => '=12', 19 => '=13', + 20 => '=14', 21 => '=15', 22 => '=16', 23 => '=17', 24 => '=18', + 25 => '=19', 26 => '=1A', 27 => '=1B', 28 => '=1C', 29 => '=1D', + 30 => '=1E', 31 => '=1F', 32 => '=20', 33 => '=21', 34 => '=22', + 35 => '=23', 36 => '=24', 37 => '=25', 38 => '=26', 39 => '=27', + 40 => '=28', 41 => '=29', 42 => '=2A', 43 => '=2B', 44 => '=2C', + 45 => '=2D', 46 => '=2E', 47 => '=2F', 48 => '=30', 49 => '=31', + 50 => '=32', 51 => '=33', 52 => '=34', 53 => '=35', 54 => '=36', + 55 => '=37', 56 => '=38', 57 => '=39', 58 => '=3A', 59 => '=3B', + 60 => '=3C', 61 => '=3D', 62 => '=3E', 63 => '=3F', 64 => '=40', + 65 => '=41', 66 => '=42', 67 => '=43', 68 => '=44', 69 => '=45', + 70 => '=46', 71 => '=47', 72 => '=48', 73 => '=49', 74 => '=4A', + 75 => '=4B', 76 => '=4C', 77 => '=4D', 78 => '=4E', 79 => '=4F', + 80 => '=50', 81 => '=51', 82 => '=52', 83 => '=53', 84 => '=54', + 85 => '=55', 86 => '=56', 87 => '=57', 88 => '=58', 89 => '=59', + 90 => '=5A', 91 => '=5B', 92 => '=5C', 93 => '=5D', 94 => '=5E', + 95 => '=5F', 96 => '=60', 97 => '=61', 98 => '=62', 99 => '=63', + 100 => '=64', 101 => '=65', 102 => '=66', 103 => '=67', 104 => '=68', + 105 => '=69', 106 => '=6A', 107 => '=6B', 108 => '=6C', 109 => '=6D', + 110 => '=6E', 111 => '=6F', 112 => '=70', 113 => '=71', 114 => '=72', + 115 => '=73', 116 => '=74', 117 => '=75', 118 => '=76', 119 => '=77', + 120 => '=78', 121 => '=79', 122 => '=7A', 123 => '=7B', 124 => '=7C', + 125 => '=7D', 126 => '=7E', 127 => '=7F', 128 => '=80', 129 => '=81', + 130 => '=82', 131 => '=83', 132 => '=84', 133 => '=85', 134 => '=86', + 135 => '=87', 136 => '=88', 137 => '=89', 138 => '=8A', 139 => '=8B', + 140 => '=8C', 141 => '=8D', 142 => '=8E', 143 => '=8F', 144 => '=90', + 145 => '=91', 146 => '=92', 147 => '=93', 148 => '=94', 149 => '=95', + 150 => '=96', 151 => '=97', 152 => '=98', 153 => '=99', 154 => '=9A', + 155 => '=9B', 156 => '=9C', 157 => '=9D', 158 => '=9E', 159 => '=9F', + 160 => '=A0', 161 => '=A1', 162 => '=A2', 163 => '=A3', 164 => '=A4', + 165 => '=A5', 166 => '=A6', 167 => '=A7', 168 => '=A8', 169 => '=A9', + 170 => '=AA', 171 => '=AB', 172 => '=AC', 173 => '=AD', 174 => '=AE', + 175 => '=AF', 176 => '=B0', 177 => '=B1', 178 => '=B2', 179 => '=B3', + 180 => '=B4', 181 => '=B5', 182 => '=B6', 183 => '=B7', 184 => '=B8', + 185 => '=B9', 186 => '=BA', 187 => '=BB', 188 => '=BC', 189 => '=BD', + 190 => '=BE', 191 => '=BF', 192 => '=C0', 193 => '=C1', 194 => '=C2', + 195 => '=C3', 196 => '=C4', 197 => '=C5', 198 => '=C6', 199 => '=C7', + 200 => '=C8', 201 => '=C9', 202 => '=CA', 203 => '=CB', 204 => '=CC', + 205 => '=CD', 206 => '=CE', 207 => '=CF', 208 => '=D0', 209 => '=D1', + 210 => '=D2', 211 => '=D3', 212 => '=D4', 213 => '=D5', 214 => '=D6', + 215 => '=D7', 216 => '=D8', 217 => '=D9', 218 => '=DA', 219 => '=DB', + 220 => '=DC', 221 => '=DD', 222 => '=DE', 223 => '=DF', 224 => '=E0', + 225 => '=E1', 226 => '=E2', 227 => '=E3', 228 => '=E4', 229 => '=E5', + 230 => '=E6', 231 => '=E7', 232 => '=E8', 233 => '=E9', 234 => '=EA', + 235 => '=EB', 236 => '=EC', 237 => '=ED', 238 => '=EE', 239 => '=EF', + 240 => '=F0', 241 => '=F1', 242 => '=F2', 243 => '=F3', 244 => '=F4', + 245 => '=F5', 246 => '=F6', 247 => '=F7', 248 => '=F8', 249 => '=F9', + 250 => '=FA', 251 => '=FB', 252 => '=FC', 253 => '=FD', 254 => '=FE', + 255 => '=FF' + ); + + protected static $_safeMapShare = array(); + + /** + * A map of non-encoded ascii characters. + * @var string[] + * @access protected + */ + protected $_safeMap = array(); + + /** + * Creates a new QpEncoder for the given CharacterStream. + * @param Swift_CharacterStream $charStream to use for reading characters + * @param Swift_StreamFilter $filter if input should be canonicalized + */ + public function __construct(Swift_CharacterStream $charStream, + Swift_StreamFilter $filter = null) + { + $this->_charStream = $charStream; + if(!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) + { + $this->initSafeMap(); + self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap; + } + else + { + $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()]; + } + $this->_filter = $filter; + } + + public function __sleep() + { + return array('_charStream', '_filter'); + } + + public function __wakeup() + { + if(!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) + { + $this->initSafeMap(); + self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap; + } + else + { + $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()]; + } + } + + protected function getSafeMapShareId() + { + return get_class($this); + } + + protected function initSafeMap() + { + foreach (array_merge( + array(0x09, 0x20), range(0x21, 0x3C), range(0x3E, 0x7E)) as $byte) + { + $this->_safeMap[$byte] = chr($byte); + } + } + + /** + * Takes an unencoded string and produces a QP encoded string from it. + * QP encoded strings have a maximum line length of 76 characters. + * If the first line needs to be shorter, indicate the difference with + * $firstLineOffset. + * @param string $string to encode + * @param int $firstLineOffset, optional + * @param int $maxLineLength, optional, 0 indicates the default of 76 chars + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, + $maxLineLength = 0) + { + if ($maxLineLength > 76 || $maxLineLength <= 0) + { + $maxLineLength = 76; + } + + $thisLineLength = $maxLineLength - $firstLineOffset; + + $lines = array(); + $lNo = 0; + $lines[$lNo] = ''; + $currentLine =& $lines[$lNo++]; + $size=$lineLen=0; + + $this->_charStream->flushContents(); + $this->_charStream->importString($string); + + //Fetching more than 4 chars at one is slower, as is fetching fewer bytes + // Conveniently 4 chars is the UTF-8 safe number since UTF-8 has up to 6 + // bytes per char and (6 * 4 * 3 = 72 chars per line) * =NN is 3 bytes + while (false !== $bytes = $this->_nextSequence()) + { + //If we're filtering the input + if (isset($this->_filter)) + { + //If we can't filter because we need more bytes + while ($this->_filter->shouldBuffer($bytes)) + { + //Then collect bytes into the buffer + if (false === $moreBytes = $this->_nextSequence(1)) + { + break; + } + + foreach ($moreBytes as $b) + { + $bytes[] = $b; + } + } + //And filter them + $bytes = $this->_filter->filter($bytes); + } + + $enc = $this->_encodeByteSequence($bytes, $size); + if ($currentLine && $lineLen+$size >= $thisLineLength) + { + $lines[$lNo] = ''; + $currentLine =& $lines[$lNo++]; + $thisLineLength = $maxLineLength; + $lineLen=0; + } + $lineLen+=$size; + $currentLine .= $enc; + } + + return $this->_standardize(implode("=\r\n", $lines)); + } + + /** + * Updates the charset used. + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_charStream->setCharacterSet($charset); + } + + // -- Protected methods + + /** + * Encode the given byte array into a verbatim QP form. + * @param int[] $bytes + * @return string + * @access protected + */ + protected function _encodeByteSequence(array $bytes, &$size) + { + $ret = ''; + $size=0; + foreach ($bytes as $b) + { + if (isset($this->_safeMap[$b])) + { + $ret .= $this->_safeMap[$b]; + ++$size; + } + else + { + $ret .= self::$_qpMap[$b]; + $size+=3; + } + } + return $ret; + } + + /** + * Get the next sequence of bytes to read from the char stream. + * @param int $size number of bytes to read + * @return int[] + * @access protected + */ + protected function _nextSequence($size = 4) + { + return $this->_charStream->readBytes($size); + } + + /** + * Make sure CRLF is correct and HT/SPACE are in valid places. + * @param string $string + * @return string + * @access protected + */ + protected function _standardize($string) + { + $string = str_replace(array("\t=0D=0A", " =0D=0A", "=0D=0A"), + array("=09\r\n", "=20\r\n", "\r\n"), $string + ); + switch ($end = ord(substr($string, -1))) + { + case 0x09: + case 0x20: + $string = substr_replace($string, self::$_qpMap[$end], -1); + } + return $string; + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php new file mode 100644 index 0000000..c4c5725 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php @@ -0,0 +1,87 @@ +_charStream = $charStream; + } + + /** + * Takes an unencoded string and produces a string encoded according to + * RFC 2231 from it. + * @param string $string to encode + * @param int $firstLineOffset + * @param int $maxLineLength, optional, 0 indicates the default of 75 bytes + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, + $maxLineLength = 0) + { + $lines = array(); $lineCount = 0; + $lines[] = ''; + $currentLine =& $lines[$lineCount++]; + + if (0 >= $maxLineLength) + { + $maxLineLength = 75; + } + + $this->_charStream->flushContents(); + $this->_charStream->importString($string); + + $thisLineLength = $maxLineLength - $firstLineOffset; + + while (false !== $char = $this->_charStream->read(4)) + { + $encodedChar = rawurlencode($char); + if (0 != strlen($currentLine) + && strlen($currentLine . $encodedChar) > $thisLineLength) + { + $lines[] = ''; + $currentLine =& $lines[$lineCount++]; + $thisLineLength = $maxLineLength; + } + $currentLine .= $encodedChar; + } + + return implode("\r\n", $lines); + } + + /** + * Updates the charset used. + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_charStream->setCharacterSet($charset); + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php new file mode 100644 index 0000000..369b9a5 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php @@ -0,0 +1,69 @@ +lookup($key); + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php new file mode 100644 index 0000000..11b808c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php @@ -0,0 +1,65 @@ +_command = $command; + $this->_successCodes = $successCodes; + } + + /** + * Get the command which was sent to the server. + * @return string + */ + public function getCommand() + { + return $this->_command; + } + + /** + * Get the numeric response codes which indicate success for this command. + * @return int[] + */ + public function getSuccessCodes() + { + return $this->_successCodes; + } + +} \ No newline at end of file diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php new file mode 100644 index 0000000..0964577 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php @@ -0,0 +1,27 @@ +_source = $source; + } + + /** + * Get the source object of this event. + * @return object + */ + public function getSource() + { + return $this->_source; + } + + /** + * Prevent this Event from bubbling any further up the stack. + * @param boolean $cancel, optional + */ + public function cancelBubble($cancel = true) + { + $this->_bubbleCancelled = $cancel; + } + + /** + * Returns true if this Event will not bubble any further up the stack. + * @return boolean + */ + public function bubbleCancelled() + { + return $this->_bubbleCancelled; + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php new file mode 100644 index 0000000..7ce0ad6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php @@ -0,0 +1,64 @@ +_response = $response; + $this->_valid = $valid; + } + + /** + * Get the response which was received from the server. + * @return string + */ + public function getResponse() + { + return $this->_response; + } + + /** + * Get the success status of this Event. + * @return boolean + */ + public function isValid() + { + return $this->_valid; + } + +} \ No newline at end of file diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php new file mode 100644 index 0000000..d2fc8e4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php @@ -0,0 +1,27 @@ +_message = $message; + $this->_result = self::RESULT_PENDING; + } + + /** + * Get the Transport used to send the Message. + * @return Swift_Transport + */ + public function getTransport() + { + return $this->getSource(); + } + + /** + * Get the Message being sent. + * @return Swift_Mime_Message + */ + public function getMessage() + { + return $this->_message; + } + + /** + * Set the array of addresses that failed in sending. + * @param array $recipients + */ + public function setFailedRecipients($recipients) + { + $this->_failedRecipients = $recipients; + } + + /** + * Get an recipient addresses which were not accepted for delivery. + * @return string[] + */ + public function getFailedRecipients() + { + return $this->_failedRecipients; + } + + /** + * Set the result of sending. + * @return int + */ + public function setResult($result) + { + $this->_result = $result; + } + + /** + * Get the result of this Event. + * The return value is a bitmask from + * {@link RESULT_PENDING, RESULT_SUCCESS, RESULT_TENTATIVE, RESULT_FAILED} + * @return int + */ + public function getResult() + { + return $this->_result; + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php new file mode 100644 index 0000000..1ce187c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php @@ -0,0 +1,33 @@ +_eventMap = array( + 'Swift_Events_CommandEvent' => 'Swift_Events_CommandListener', + 'Swift_Events_ResponseEvent' => 'Swift_Events_ResponseListener', + 'Swift_Events_SendEvent' => 'Swift_Events_SendListener', + 'Swift_Events_TransportChangeEvent' => 'Swift_Events_TransportChangeListener', + 'Swift_Events_TransportExceptionEvent' => 'Swift_Events_TransportExceptionListener' + ); + } + + /** + * Create a new SendEvent for $source and $message. + * + * @param Swift_Transport $source + * @param Swift_Mime_Message + * @return Swift_Events_SendEvent + */ + public function createSendEvent(Swift_Transport $source, + Swift_Mime_Message $message) + { + return new Swift_Events_SendEvent($source, $message); + } + + /** + * Create a new CommandEvent for $source and $command. + * + * @param Swift_Transport $source + * @param string $command That will be executed + * @param array $successCodes That are needed + * @return Swift_Events_CommandEvent + */ + public function createCommandEvent(Swift_Transport $source, + $command, $successCodes = array()) + { + return new Swift_Events_CommandEvent($source, $command, $successCodes); + } + + /** + * Create a new ResponseEvent for $source and $response. + * + * @param Swift_Transport $source + * @param string $response + * @param boolean $valid If the response is valid + * @return Swift_Events_ResponseEvent + */ + public function createResponseEvent(Swift_Transport $source, + $response, $valid) + { + return new Swift_Events_ResponseEvent($source, $response, $valid); + } + + /** + * Create a new TransportChangeEvent for $source. + * + * @param Swift_Transport $source + * @return Swift_Events_TransportChangeEvent + */ + public function createTransportChangeEvent(Swift_Transport $source) + { + return new Swift_Events_TransportChangeEvent($source); + } + + /** + * Create a new TransportExceptionEvent for $source. + * + * @param Swift_Transport $source + * @param Swift_TransportException $ex + * @return Swift_Events_TransportExceptionEvent + */ + public function createTransportExceptionEvent(Swift_Transport $source, + Swift_TransportException $ex) + { + return new Swift_Events_TransportExceptionEvent($source, $ex); + } + + /** + * Bind an event listener to this dispatcher. + * + * @param Swift_Events_EventListener $listener + */ + public function bindEventListener(Swift_Events_EventListener $listener) + { + foreach ($this->_listeners as $l) + { + //Already loaded + if ($l === $listener) + { + return; + } + } + $this->_listeners[] = $listener; + } + + /** + * Dispatch the given Event to all suitable listeners. + * + * @param Swift_Events_EventObject $evt + * @param string $target method + */ + public function dispatchEvent(Swift_Events_EventObject $evt, $target) + { + $this->_prepareBubbleQueue($evt); + $this->_bubble($evt, $target); + } + + // -- Private methods + + /** Queue listeners on a stack ready for $evt to be bubbled up it */ + private function _prepareBubbleQueue(Swift_Events_EventObject $evt) + { + $this->_bubbleQueue = array(); + $evtClass = get_class($evt); + foreach ($this->_listeners as $listener) + { + if (array_key_exists($evtClass, $this->_eventMap) + && ($listener instanceof $this->_eventMap[$evtClass])) + { + $this->_bubbleQueue[] = $listener; + } + } + } + + /** Bubble $evt up the stack calling $target() on each listener */ + private function _bubble(Swift_Events_EventObject $evt, $target) + { + if (!$evt->bubbleCancelled() && $listener = array_shift($this->_bubbleQueue)) + { + $listener->$target($evt); + $this->_bubble($evt, $target); + } + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php new file mode 100644 index 0000000..e57176a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php @@ -0,0 +1,30 @@ +getSource(); + } + +} \ No newline at end of file diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php new file mode 100644 index 0000000..9c028f0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php @@ -0,0 +1,51 @@ +_exception = $ex; + } + + /** + * Get the TransportException thrown. + * @return Swift_TransportException + */ + public function getException() + { + return $this->_exception; + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php new file mode 100644 index 0000000..92266af --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php @@ -0,0 +1,28 @@ +createDependenciesFor('transport.failover') + ); + + $this->setTransports($transports); + } + + /** + * Create a new FailoverTransport instance. + * @param string $transports + * @return Swift_FailoverTransport + */ + public static function newInstance($transports = array()) + { + return new self($transports); + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php new file mode 100644 index 0000000..48b2247 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php @@ -0,0 +1,210 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages on the filesystem. + * @package Swift + * @author Fabien Potencier + * @author Xavier De Cock + */ +class Swift_FileSpool extends Swift_ConfigurableSpool +{ + /** The spool directory */ + private $_path; + + /** + * File WriteRetry Limit + * @var int + */ + private $_retryLimit=10; + + /** + * Create a new FileSpool. + * @param string $path + * @throws Swift_IoException + */ + public function __construct($path) + { + $this->_path = $path; + + if (!file_exists($this->_path)) + { + if (!mkdir($this->_path, 0777, true)) + { + throw new Swift_IoException('Unable to create Path ['.$this->_path.']'); + } + } + } + + /** + * Tests if this Spool mechanism has started. + * + * @return boolean + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Spool mechanism. + */ + public function start() + { + } + + /** + * Stops this Spool mechanism. + */ + public function stop() + { + } + + /** + * Allow to manage the enqueuing retry limit. + * Default, is ten and allows over 64^20 different fileNames + * + * @param integer $limit + */ + public function setRetryLimit($limit) + { + $this->_retryLimit=$limit; + } + + /** + * Queues a message. + * @param Swift_Mime_Message $message The message to store + * @return boolean + * @throws Swift_IoException + */ + public function queueMessage(Swift_Mime_Message $message) + { + $ser = serialize($message); + $fileName=$this->_path.'/'.$this->getRandomString(10); + for ($i = 0; $i < $this->_retryLimit; ++$i) + { + /* We try an exclusive creation of the file + * This is an atomic operation, it avoid locking mechanism + */ + $fp = @fopen($fileName.'.message', 'x'); + if (false !== $fp) + { + if (false === fwrite($fp, $ser)) + { + return false; + } + + return fclose($fp); + } + else + { + /* The file allready exists, we try a longer fileName + */ + $fileName.=$this->getRandomString(1); + } + } + + throw new Swift_IoException('Unable to create a file for enqueuing Message'); + } + + /** + * Execute a recovery if for anyreason a process is sending for too long + * + * @param int $timeout in second Defaults is for very slow smtp responses + */ + public function recover($timeout=900) + { + foreach (new DirectoryIterator($this->_path) as $file) + { + $file = $file->getRealPath(); + + if (substr($file, -16)=='.message.sending') + { + $lockedtime=filectime($file); + if ((time()-$lockedtime)>$timeout) + { + rename($file, substr($file, 0, -8)); + } + } + } + } + + /** + * Sends messages using the given transport instance. + * + * @param Swift_Transport $transport A transport instance + * @param string[] &$failedRecipients An array of failures by-reference + * + * @return int The number of sent emails + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null) + { + if (!$transport->isStarted()) + { + $transport->start(); + } + + $failedRecipients = (array) $failedRecipients; + $count = 0; + $time = time(); + foreach (new DirectoryIterator($this->_path) as $file) + { + $file = $file->getRealPath(); + + if (substr($file, -8) != '.message') + { + continue; + } + + /* We try a rename, it's an atomic operation, and avoid locking the file */ + if (rename($file, $file.'.sending')) + { + $message = unserialize(file_get_contents($file.'.sending')); + + $count += $transport->send($message, $failedRecipients); + + unlink($file.'.sending'); + } + else + { + /* This message has just been catched by another process */ + continue; + } + + if ($this->getMessageLimit() && $count >= $this->getMessageLimit()) + { + break; + } + + if ($this->getTimeLimit() && (time() - $time) >= $this->getTimeLimit()) + { + break; + } + } + + return $count; + } + + /** + * Returns a random string needed to generate a fileName for the queue. + * @param int $count + */ + protected function getRandomString($count) { + // This string MUST stay FS safe, avoid special chars + $base="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-."; + $ret=''; + $strlen=strlen($base); + for ($i=0; $i<$count; ++$i) + { + $ret.=$base[((int)rand(0,$strlen-1))]; + } + return $ret; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php new file mode 100644 index 0000000..56bf36e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php @@ -0,0 +1,27 @@ +setFile( + new Swift_ByteStream_FileByteStream($path) + ); + return $image; + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php new file mode 100644 index 0000000..e8f45f4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php @@ -0,0 +1,72 @@ +_stream = $stream; + } + + /** + * Set a string into the cache under $itemKey for the namespace $nsKey. + * @param string $nsKey + * @param string $itemKey + * @param string $string + * @param int $mode + * @see MODE_WRITE, MODE_APPEND + */ + public function setString($nsKey, $itemKey, $string, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) + { + case self::MODE_WRITE: + $this->_contents[$nsKey][$itemKey] = $string; + break; + case self::MODE_APPEND: + if (!$this->hasKey($nsKey, $itemKey)) + { + $this->_contents[$nsKey][$itemKey] = ''; + } + $this->_contents[$nsKey][$itemKey] .= $string; + break; + default: + throw new Swift_SwiftException( + 'Invalid mode [' . $mode . '] used to set nsKey='. + $nsKey . ', itemKey=' . $itemKey + ); + } + } + + /** + * Set a ByteStream into the cache under $itemKey for the namespace $nsKey. + * @param string $nsKey + * @param string $itemKey + * @param Swift_OutputByteStream $os + * @param int $mode + * @see MODE_WRITE, MODE_APPEND + */ + public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, + $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) + { + case self::MODE_WRITE: + $this->clearKey($nsKey, $itemKey); + case self::MODE_APPEND: + if (!$this->hasKey($nsKey, $itemKey)) + { + $this->_contents[$nsKey][$itemKey] = ''; + } + while (false !== $bytes = $os->read(8192)) + { + $this->_contents[$nsKey][$itemKey] .= $bytes; + } + break; + default: + throw new Swift_SwiftException( + 'Invalid mode [' . $mode . '] used to set nsKey='. + $nsKey . ', itemKey=' . $itemKey + ); + } + } + + /** + * Provides a ByteStream which when written to, writes data to $itemKey. + * NOTE: The stream will always write in append mode. + * @param string $nsKey + * @param string $itemKey + * @return Swift_InputByteStream + */ + public function getInputByteStream($nsKey, $itemKey, + Swift_InputByteStream $writeThrough = null) + { + $is = clone $this->_stream; + $is->setKeyCache($this); + $is->setNsKey($nsKey); + $is->setItemKey($itemKey); + if (isset($writeThrough)) + { + $is->setWriteThroughStream($writeThrough); + } + return $is; + } + + /** + * Get data back out of the cache as a string. + * @param string $nsKey + * @param string $itemKey + * @return string + */ + public function getString($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + if ($this->hasKey($nsKey, $itemKey)) + { + return $this->_contents[$nsKey][$itemKey]; + } + } + + /** + * Get data back out of the cache as a ByteStream. + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $is to write the data to + */ + public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is) + { + $this->_prepareCache($nsKey); + $is->write($this->getString($nsKey, $itemKey)); + } + + /** + * Check if the given $itemKey exists in the namespace $nsKey. + * @param string $nsKey + * @param string $itemKey + * @return boolean + */ + public function hasKey($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + return array_key_exists($itemKey, $this->_contents[$nsKey]); + } + + /** + * Clear data for $itemKey in the namespace $nsKey if it exists. + * @param string $nsKey + * @param string $itemKey + */ + public function clearKey($nsKey, $itemKey) + { + unset($this->_contents[$nsKey][$itemKey]); + } + + /** + * Clear all data in the namespace $nsKey if it exists. + * @param string $nsKey + */ + public function clearAll($nsKey) + { + unset($this->_contents[$nsKey]); + } + + // -- Private methods + + /** + * Initialize the namespace of $nsKey if needed. + * @param string $nsKey + * @access private + */ + private function _prepareCache($nsKey) + { + if (!array_key_exists($nsKey, $this->_contents)) + { + $this->_contents[$nsKey] = array(); + } + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php new file mode 100644 index 0000000..7835079 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php @@ -0,0 +1,333 @@ +_stream = $stream; + $this->_path = $path; + + if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) + { + $this->_quotes = true; + } + } + + /** + * Set a string into the cache under $itemKey for the namespace $nsKey. + * @param string $nsKey + * @param string $itemKey + * @param string $string + * @param int $mode + * @throws Swift_IoException + * @see MODE_WRITE, MODE_APPEND + */ + public function setString($nsKey, $itemKey, $string, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) + { + case self::MODE_WRITE: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + break; + case self::MODE_APPEND: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_END); + break; + default: + throw new Swift_SwiftException( + 'Invalid mode [' . $mode . '] used to set nsKey='. + $nsKey . ', itemKey=' . $itemKey + ); + break; + } + fwrite($fp, $string); + $this->_freeHandle($nsKey, $itemKey); + } + + /** + * Set a ByteStream into the cache under $itemKey for the namespace $nsKey. + * @param string $nsKey + * @param string $itemKey + * @param Swift_OutputByteStream $os + * @param int $mode + * @see MODE_WRITE, MODE_APPEND + * @throws Swift_IoException + */ + public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, + $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) + { + case self::MODE_WRITE: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + break; + case self::MODE_APPEND: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_END); + break; + default: + throw new Swift_SwiftException( + 'Invalid mode [' . $mode . '] used to set nsKey='. + $nsKey . ', itemKey=' . $itemKey + ); + break; + } + while (false !== $bytes = $os->read(8192)) + { + fwrite($fp, $bytes); + } + $this->_freeHandle($nsKey, $itemKey); + } + + /** + * Provides a ByteStream which when written to, writes data to $itemKey. + * NOTE: The stream will always write in append mode. + * @param string $nsKey + * @param string $itemKey + * @return Swift_InputByteStream + */ + public function getInputByteStream($nsKey, $itemKey, + Swift_InputByteStream $writeThrough = null) + { + $is = clone $this->_stream; + $is->setKeyCache($this); + $is->setNsKey($nsKey); + $is->setItemKey($itemKey); + if (isset($writeThrough)) + { + $is->setWriteThroughStream($writeThrough); + } + return $is; + } + + /** + * Get data back out of the cache as a string. + * @param string $nsKey + * @param string $itemKey + * @return string + * @throws Swift_IoException + */ + public function getString($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + if ($this->hasKey($nsKey, $itemKey)) + { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + if ($this->_quotes) + { + ini_set('magic_quotes_runtime', 0); + } + $str = ''; + while (!feof($fp) && false !== $bytes = fread($fp, 8192)) + { + $str .= $bytes; + } + if ($this->_quotes) + { + ini_set('magic_quotes_runtime', 1); + } + $this->_freeHandle($nsKey, $itemKey); + return $str; + } + } + + /** + * Get data back out of the cache as a ByteStream. + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $is to write the data to + */ + public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is) + { + if ($this->hasKey($nsKey, $itemKey)) + { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + if ($this->_quotes) + { + ini_set('magic_quotes_runtime', 0); + } + while (!feof($fp) && false !== $bytes = fread($fp, 8192)) + { + $is->write($bytes); + } + if ($this->_quotes) + { + ini_set('magic_quotes_runtime', 1); + } + $this->_freeHandle($nsKey, $itemKey); + } + } + + /** + * Check if the given $itemKey exists in the namespace $nsKey. + * @param string $nsKey + * @param string $itemKey + * @return boolean + */ + public function hasKey($nsKey, $itemKey) + { + return is_file($this->_path . '/' . $nsKey . '/' . $itemKey); + } + + /** + * Clear data for $itemKey in the namespace $nsKey if it exists. + * @param string $nsKey + * @param string $itemKey + */ + public function clearKey($nsKey, $itemKey) + { + if ($this->hasKey($nsKey, $itemKey)) + { + $this->_freeHandle($nsKey, $itemKey); + unlink($this->_path . '/' . $nsKey . '/' . $itemKey); + } + } + + /** + * Clear all data in the namespace $nsKey if it exists. + * @param string $nsKey + */ + public function clearAll($nsKey) + { + if (array_key_exists($nsKey, $this->_keys)) + { + foreach ($this->_keys[$nsKey] as $itemKey=>$null) + { + $this->clearKey($nsKey, $itemKey); + } + if (is_dir($this->_path . '/' . $nsKey)) + { + rmdir($this->_path . '/' . $nsKey); + } + unset($this->_keys[$nsKey]); + } + } + + // -- Private methods + + /** + * Initialize the namespace of $nsKey if needed. + * @param string $nsKey + * @access private + */ + private function _prepareCache($nsKey) + { + $cacheDir = $this->_path . '/' . $nsKey; + if (!is_dir($cacheDir)) + { + if (!mkdir($cacheDir)) + { + throw new Swift_IoException('Failed to create cache directory ' . $cacheDir); + } + $this->_keys[$nsKey] = array(); + } + } + + /** + * Get a file handle on the cache item. + * @param string $nsKey + * @param string $itemKey + * @param int $position + * @return resource + * @access private + */ + private function _getHandle($nsKey, $itemKey, $position) + { + if (!isset($this->_keys[$nsKey][$itemKey])) + { + $openMode = $this->hasKey($nsKey, $itemKey) + ? 'r+b' + : 'w+b' + ; + $fp = fopen($this->_path . '/' . $nsKey . '/' . $itemKey, $openMode); + $this->_keys[$nsKey][$itemKey] = $fp; + } + if (self::POSITION_START == $position) + { + fseek($this->_keys[$nsKey][$itemKey], 0, SEEK_SET); + } + elseif (self::POSITION_END == $position) + { + fseek($this->_keys[$nsKey][$itemKey], 0, SEEK_END); + } + return $this->_keys[$nsKey][$itemKey]; + } + + private function _freeHandle($nsKey, $itemKey) + { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_CURRENT); + fclose($fp); + $this->_keys[$nsKey][$itemKey] = null; + } + + /** + * Destructor. + */ + public function __destruct() + { + foreach ($this->_keys as $nsKey=>$null) + { + $this->clearAll($nsKey); + } + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DummyKeyCache.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DummyKeyCache.php new file mode 100644 index 0000000..5721c66 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DummyKeyCache.php @@ -0,0 +1,112 @@ +. + + */ + +/** + * A basic KeyCache backed by an array. + * @package Swift + * @subpackage KeyCache + * @author Xavier De Cock + */ +class Swift_KeyCache_DummyKeyCache implements Swift_KeyCache +{ + /** + * Set a string into the cache under $itemKey for the namespace $nsKey. + * @param string $nsKey + * @param string $itemKey + * @param string $string + * @param int $mode + * @see MODE_WRITE, MODE_APPEND + */ + public function setString($nsKey, $itemKey, $string, $mode) + {} + + /** + * Set a ByteStream into the cache under $itemKey for the namespace $nsKey. + * @param string $nsKey + * @param string $itemKey + * @param Swift_OutputByteStream $os + * @param int $mode + * @see MODE_WRITE, MODE_APPEND + */ + public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, + $mode) + {} + + /** + * Provides a ByteStream which when written to, writes data to $itemKey. + * NOTE: The stream will always write in append mode. + * @param string $nsKey + * @param string $itemKey + * @return Swift_InputByteStream + */ + public function getInputByteStream($nsKey, $itemKey, + Swift_InputByteStream $writeThrough = null) + { + return false; + } + + /** + * Get data back out of the cache as a string. + * @param string $nsKey + * @param string $itemKey + * @return string + */ + public function getString($nsKey, $itemKey) + { + return false; + } + + /** + * Get data back out of the cache as a ByteStream. + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $is to write the data to + */ + public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is) + { + return false; + } + + /** + * Check if the given $itemKey exists in the namespace $nsKey. + * @param string $nsKey + * @param string $itemKey + * @return boolean + */ + public function hasKey($nsKey, $itemKey) + { + return false; + } + + /** + * Clear data for $itemKey in the namespace $nsKey if it exists. + * @param string $nsKey + * @param string $itemKey + */ + public function clearKey($nsKey, $itemKey) + {} + + /** + * Clear all data in the namespace $nsKey if it exists. + * @param string $nsKey + */ + public function clearAll($nsKey) + {} +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php new file mode 100644 index 0000000..f4a3f47 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php @@ -0,0 +1,51 @@ +_keyCache = $keyCache; + } + + /** + * Specify a stream to write through for each write(). + * @param Swift_InputByteStream $is + */ + public function setWriteThroughStream(Swift_InputByteStream $is) + { + $this->_writeThrough = $is; + } + + /** + * Writes $bytes to the end of the stream. + * @param string $bytes + * @param Swift_InputByteStream $is, optional + */ + public function write($bytes, Swift_InputByteStream $is = null) + { + $this->_keyCache->setString( + $this->_nsKey, $this->_itemKey, $bytes, Swift_KeyCache::MODE_APPEND + ); + if (isset($is)) + { + $is->write($bytes); + } + if (isset($this->_writeThrough)) + { + $this->_writeThrough->write($bytes); + } + } + + /** + * Not used. + */ + public function commit() + { + } + + /** + * Not used. + */ + public function bind(Swift_InputByteStream $is) + { + } + + /** + * Not used. + */ + public function unbind(Swift_InputByteStream $is) + { + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + */ + public function flushBuffers() + { + $this->_keyCache->clearKey($this->_nsKey, $this->_itemKey); + } + + /** + * Set the nsKey which will be written to. + * @param string $nsKey + */ + public function setNsKey($nsKey) + { + $this->_nsKey = $nsKey; + } + + /** + * Set the itemKey which will be written to. + * @param string $itemKey + */ + public function setItemKey($itemKey) + { + $this->_itemKey = $itemKey; + } + + /** + * Any implementation should be cloneable, allowing the clone to access a + * separate $nsKey and $itemKey. + */ + public function __clone() + { + $this->_writeThrough = null; + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php new file mode 100644 index 0000000..de3045a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php @@ -0,0 +1,46 @@ +createDependenciesFor('transport.loadbalanced') + ); + + $this->setTransports($transports); + } + + /** + * Create a new LoadBalancedTransport instance. + * @param string $transports + * @return Swift_LoadBalancedTransport + */ + public static function newInstance($transports = array()) + { + return new self($transports); + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php new file mode 100644 index 0000000..dd8ccac --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php @@ -0,0 +1,46 @@ +createDependenciesFor('transport.mail') + ); + + $this->setExtraParams($extraParams); + } + + /** + * Create a new MailTransport instance. + * @param string $extraParams To be passed to mail() + * @return Swift_MailTransport + */ + public static function newInstance($extraParams = '-f%s') + { + return new self($extraParams); + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php new file mode 100644 index 0000000..a0561d3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php @@ -0,0 +1,118 @@ +_transport = $transport; + } + + /** + * Create a new Mailer instance. + * + * @param Swift_Transport $transport + * @return Swift_Mailer + */ + public static function newInstance(Swift_Transport $transport) + { + return new self($transport); + } + + /** + * Create a new class instance of one if the message services + * For example 'mimepart' would create a 'message.mimepart' instance + * + * @param string $service + * @return object + */ + public function createMessage($service = 'message') + { + return Swift_DependencyContainer::getInstance() + ->lookup('message.'.$service); + } + + /** + * Send the given Message like it would be sent in a mail client. + * + * All recipients (with the exception of Bcc) will be able to see the other + * recipients this message was sent to. + * + * Recipient/sender data will be retrieved from the Message object. + * + * The return value is the number of recipients who were accepted for + * delivery. + * + * @param Swift_Mime_Message $message + * @param array &$failedRecipients, optional + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $failedRecipients = (array) $failedRecipients; + + if (!$this->_transport->isStarted()) + { + $this->_transport->start(); + } + + $sent = 0; + + try + { + $sent = $this->_transport->send($message, $failedRecipients); + } + catch (Swift_RfcComplianceException $e) + { + foreach ($message->getTo() as $address => $name) + { + $failedRecipients[] = $address; + } + } + + return $sent; + } + + /** + * Register a plugin using a known unique key (e.g. myPlugin). + * + * @param Swift_Events_EventListener $plugin + * @param string $key + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_transport->registerPlugin($plugin); + } + + /** + * The Transport used to send messages. + * @return Swift_Transport + */ + public function getTransport() + { + return $this->_transport; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php new file mode 100644 index 0000000..c732ebb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php @@ -0,0 +1,58 @@ +_recipients = $recipients; + } + + /** + * Returns true only if there are more recipients to send to. + * @return boolean + */ + public function hasNext() + { + return !empty($this->_recipients); + } + + /** + * Returns an array where the keys are the addresses of recipients and the + * values are the names. + * e.g. ('foo@bar' => 'Foo') or ('foo@bar' => NULL) + * @return array + */ + public function nextRecipient() + { + return array_splice($this->_recipients, 0, 1); + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php new file mode 100644 index 0000000..2713841 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php @@ -0,0 +1,34 @@ + 'Foo') or ('foo@bar' => NULL) + * @return array + */ + public function nextRecipient(); + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php new file mode 100644 index 0000000..6a64c52 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in memory. + * @package Swift + * @author Fabien Potencier + */ +class Swift_MemorySpool implements Swift_Spool +{ + protected $messages = array(); + + /** + * Tests if this Transport mechanism has started. + * @return boolean + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Transport mechanism. + */ + public function start() + { + } + + /** + * Stops this Transport mechanism. + */ + public function stop() + { + } + + /** + * Stores a message in the queue. + * + * @param Swift_Mime_Message $message The message to store + * + * @return boolean Whether the operation has succeeded + */ + public function queueMessage(Swift_Mime_Message $message) + { + $this->messages[] = $message; + return true; + } + + /** + * Sends messages using the given transport instance. + * + * @param Swift_Transport $transport A transport instance + * @param string[] &$failedRecipients An array of failures by-reference + * + * @return int The number of sent emails + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null) + { + if (!$this->messages) + { + return 0; + } + + if (!$transport->isStarted()) + { + $transport->start(); + } + + $count = 0; + while ($message = array_pop($this->messages)) + { + $count += $transport->send($message, $failedRecipients); + } + + return $count; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php new file mode 100644 index 0000000..2f12ede --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php @@ -0,0 +1,84 @@ +createDependenciesFor('mime.message') + ); + + if (!isset($charset)) + { + $charset = Swift_DependencyContainer::getInstance() + ->lookup('properties.charset'); + } + $this->setSubject($subject); + $this->setBody($body); + $this->setCharset($charset); + if ($contentType) + { + $this->setContentType($contentType); + } + } + + /** + * Create a new Message. + * @param string $subject + * @param string $body + * @param string $contentType + * @param string $charset + * @return Swift_Mime_Message + */ + public static function newInstance($subject = null, $body = null, + $contentType = null, $charset = null) + { + return new self($subject, $body, $contentType, $charset); + } + + /** + * Add a MimePart to this Message. + * @param string|Swift_OutputByteStream $body + * @param string $contentType + * @param string $charset + */ + public function addPart($body, $contentType = null, $charset = null) + { + return $this->attach(Swift_MimePart::newInstance( + $body, $contentType, $charset + )); + } + + public function __wakeup() + { + Swift_DependencyContainer::getInstance()->createDependenciesFor('mime.message'); + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php new file mode 100644 index 0000000..e1c28f4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php @@ -0,0 +1,143 @@ +setDisposition('attachment'); + $this->setContentType('application/octet-stream'); + $this->_mimeTypes = $mimeTypes; + } + + /** + * Get the nesting level used for this attachment. + * Always returns {@link LEVEL_MIXED}. + * @return int + */ + public function getNestingLevel() + { + return self::LEVEL_MIXED; + } + + /** + * Get the Content-Disposition of this attachment. + * By default attachments have a disposition of "attachment". + * @return string + */ + public function getDisposition() + { + return $this->_getHeaderFieldModel('Content-Disposition'); + } + + /** + * Set the Content-Disposition of this attachment. + * @param string $disposition + * @return Swift_Mime_Attachment + */ + public function setDisposition($disposition) + { + if (!$this->_setHeaderFieldModel('Content-Disposition', $disposition)) + { + $this->getHeaders()->addParameterizedHeader( + 'Content-Disposition', $disposition + ); + } + return $this; + } + + /** + * Get the filename of this attachment when downloaded. + * @return string + */ + public function getFilename() + { + return $this->_getHeaderParameter('Content-Disposition', 'filename'); + } + + /** + * Set the filename of this attachment. + * @param string $filename + * @return Swift_Mime_Attachment + */ + public function setFilename($filename) + { + $this->_setHeaderParameter('Content-Disposition', 'filename', $filename); + $this->_setHeaderParameter('Content-Type', 'name', $filename); + return $this; + } + + /** + * Get the file size of this attachment. + * @return int + */ + public function getSize() + { + return $this->_getHeaderParameter('Content-Disposition', 'size'); + } + + /** + * Set the file size of this attachment. + * @param int $size + * @return Swift_Mime_Attachment + */ + public function setSize($size) + { + $this->_setHeaderParameter('Content-Disposition', 'size', $size); + return $this; + } + + /** + * Set the file that this attachment is for. + * @param Swift_FileStream $file + * @param string $contentType optional + * @return Swift_Mime_Attachment + */ + public function setFile(Swift_FileStream $file, $contentType = null) + { + $this->setFilename(basename($file->getPath())); + $this->setBody($file, $contentType); + if (!isset($contentType)) + { + $extension = strtolower(substr( + $file->getPath(), strrpos($file->getPath(), '.') + 1 + )); + + if (array_key_exists($extension, $this->_mimeTypes)) + { + $this->setContentType($this->_mimeTypes[$extension]); + } + } + return $this; + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php new file mode 100644 index 0000000..c26009f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php @@ -0,0 +1,26 @@ += $maxLineLength || 76 < $maxLineLength) + { + $maxLineLength = 76; + } + + $remainder = 0; + + while (false !== $bytes = $os->read(8190)) + { + $encoded = base64_encode($bytes); + $encodedTransformed = ''; + $thisMaxLineLength = $maxLineLength - $remainder - $firstLineOffset; + + while ($thisMaxLineLength < strlen($encoded)) + { + $encodedTransformed .= substr($encoded, 0, $thisMaxLineLength) . "\r\n"; + $firstLineOffset = 0; + $encoded = substr($encoded, $thisMaxLineLength); + $thisMaxLineLength = $maxLineLength; + $remainder = 0; + } + + if (0 < $remainingLength = strlen($encoded)) + { + $remainder += $remainingLength; + $encodedTransformed .= $encoded; + $encoded = null; + } + + $is->write($encodedTransformed); + } + } + + /** + * Get the name of this encoding scheme. + * Returns the string 'base64'. + * @return string + */ + public function getName() + { + return 'base64'; + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/PlainContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/PlainContentEncoder.php new file mode 100644 index 0000000..7f5ad08 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/PlainContentEncoder.php @@ -0,0 +1,172 @@ +_name = $name; + $this->_canonical = $canonical; + } + + /** + * Encode a given string to produce an encoded string. + * @param string $string + * @param int $firstLineOffset, ignored + * @param int $maxLineLength - 0 means no wrapping will occur + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, + $maxLineLength = 0) + { + if ($this->_canonical) + { + $string = $this->_canonicalize($string); + } + return $this->_safeWordWrap($string, $maxLineLength, "\r\n"); + } + + /** + * Encode stream $in to stream $out. + * @param Swift_OutputByteStream $in + * @param Swift_InputByteStream $out + * @param int $firstLineOffset, ignored + * @param int $maxLineLength, optional, 0 means no wrapping will occur + */ + public function encodeByteStream( + Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, + $maxLineLength = 0) + { + $leftOver = ''; + while (false !== $bytes = $os->read(8192)) + { + $toencode = $leftOver . $bytes; + if ($this->_canonical) + { + $toencode = $this->_canonicalize($toencode); + } + $wrapped = $this->_safeWordWrap($toencode, $maxLineLength, "\r\n"); + $lastLinePos = strrpos($wrapped, "\r\n"); + $leftOver = substr($wrapped, $lastLinePos); + $wrapped = substr($wrapped, 0, $lastLinePos); + + $is->write($wrapped); + } + if (strlen($leftOver)) + { + $is->write($leftOver); + } + } + + /** + * Get the name of this encoding scheme. + * @return string + */ + public function getName() + { + return $this->_name; + } + + /** + * Not used. + */ + public function charsetChanged($charset) + { + } + + // -- Private methods + + /** + * A safer (but weaker) wordwrap for unicode. + * @param string $string + * @param int $length + * @param string $le + * @return string + * @access private + */ + private function _safeWordwrap($string, $length = 75, $le = "\r\n") + { + if (0 >= $length) + { + return $string; + } + + $originalLines = explode($le, $string); + + $lines = array(); + $lineCount = 0; + + foreach ($originalLines as $originalLine) + { + $lines[] = ''; + $currentLine =& $lines[$lineCount++]; + + //$chunks = preg_split('/(?<=[\ \t,\.!\?\-&\+\/])/', $originalLine); + $chunks = preg_split('/(?<=\s)/', $originalLine); + + foreach ($chunks as $chunk) + { + if (0 != strlen($currentLine) + && strlen($currentLine . $chunk) > $length) + { + $lines[] = ''; + $currentLine =& $lines[$lineCount++]; + } + $currentLine .= $chunk; + } + } + + return implode("\r\n", $lines); + } + + /** + * Canonicalize string input (fix CRLF). + * @param string $string + * @return string + * @access private + */ + private function _canonicalize($string) + { + return str_replace( + array("\r\n", "\r", "\n"), + array("\n", "\n", "\r\n"), + $string + ); + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php new file mode 100644 index 0000000..8d49370 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php @@ -0,0 +1,135 @@ +_dotEscape = $dotEscape; + parent::__construct($charStream, $filter); + } + + public function __sleep() + { + return array('_charStream', '_filter', '_dotEscape'); + } + + protected function getSafeMapShareId() + { + return get_class($this).($this->_dotEscape ? '.dotEscape' : ''); + } + + protected function initSafeMap() + { + parent::initSafeMap(); + if ($this->_dotEscape) { + /* Encode . as =2e for buggy remote servers */ + unset($this->_safeMap[0x2e]); + } + } + + /** + * Encode stream $in to stream $out. + * QP encoded strings have a maximum line length of 76 characters. + * If the first line needs to be shorter, indicate the difference with + * $firstLineOffset. + * @param Swift_OutputByteStream $os output stream + * @param Swift_InputByteStream $is input stream + * @param int $firstLineOffset + * @param int $maxLineLength + */ + public function encodeByteStream( + Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, + $maxLineLength = 0) + { + if ($maxLineLength > 76 || $maxLineLength <= 0) + { + $maxLineLength = 76; + } + + $thisLineLength = $maxLineLength - $firstLineOffset; + + $this->_charStream->flushContents(); + $this->_charStream->importByteStream($os); + + $currentLine = ''; + $prepend = ''; + $size=$lineLen=0; + + while (false !== $bytes = $this->_nextSequence()) + { + //If we're filtering the input + if (isset($this->_filter)) + { + //If we can't filter because we need more bytes + while ($this->_filter->shouldBuffer($bytes)) + { + //Then collect bytes into the buffer + if (false === $moreBytes = $this->_nextSequence(1)) + { + break; + } + + foreach ($moreBytes as $b) + { + $bytes[] = $b; + } + } + //And filter them + $bytes = $this->_filter->filter($bytes); + } + + $enc = $this->_encodeByteSequence($bytes, $size); + if ($currentLine && $lineLen+$size >= $thisLineLength) + { + $is->write($prepend . $this->_standardize($currentLine)); + $currentLine = ''; + $prepend = "=\r\n"; + $thisLineLength = $maxLineLength; + $lineLen=0; + } + $lineLen+=$size; + $currentLine .= $enc; + } + if (strlen($currentLine)) + { + $is->write($prepend . $this->_standardize($currentLine)); + } + } + + /** + * Get the name of this encoding scheme. + * Returns the string 'quoted-printable'. + * @return string + */ + public function getName() + { + return 'quoted-printable'; + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php new file mode 100644 index 0000000..529de7f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php @@ -0,0 +1,48 @@ +setDisposition('inline'); + $this->setId($this->getId()); + } + + /** + * Get the nesting level of this EmbeddedFile. + * Returns {@link LEVEL_RELATED}. + * @return int + */ + public function getNestingLevel() + { + return self::LEVEL_RELATED; + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php new file mode 100644 index 0000000..021cabd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php @@ -0,0 +1,27 @@ +init(); + } + + public function __wakeup() + { + $this->init(); + } + + protected function init() + { + if(count(self::$_specials) > 0) + { + return; + } + + self::$_specials = array( + '(', ')', '<', '>', '[', ']', + ':', ';', '@', ',', '.', '"' + ); + + /*** Refer to RFC 2822 for ABNF grammar ***/ + + //All basic building blocks + self::$_grammar['NO-WS-CTL'] = '[\x01-\x08\x0B\x0C\x0E-\x19\x7F]'; + self::$_grammar['WSP'] = '[ \t]'; + self::$_grammar['CRLF'] = '(?:\r\n)'; + self::$_grammar['FWS'] = '(?:(?:' . self::$_grammar['WSP'] . '*' . + self::$_grammar['CRLF'] . ')?' . self::$_grammar['WSP'] . ')'; + self::$_grammar['text'] = '[\x00-\x08\x0B\x0C\x0E-\x7F]'; + self::$_grammar['quoted-pair'] = '(?:\\\\' . self::$_grammar['text'] . ')'; + self::$_grammar['ctext'] = '(?:' . self::$_grammar['NO-WS-CTL'] . + '|[\x21-\x27\x2A-\x5B\x5D-\x7E])'; + //Uses recursive PCRE (?1) -- could be a weak point?? + self::$_grammar['ccontent'] = '(?:' . self::$_grammar['ctext'] . '|' . + self::$_grammar['quoted-pair'] . '|(?1))'; + self::$_grammar['comment'] = '(\((?:' . self::$_grammar['FWS'] . '|' . + self::$_grammar['ccontent']. ')*' . self::$_grammar['FWS'] . '?\))'; + self::$_grammar['CFWS'] = '(?:(?:' . self::$_grammar['FWS'] . '?' . + self::$_grammar['comment'] . ')*(?:(?:' . self::$_grammar['FWS'] . '?' . + self::$_grammar['comment'] . ')|' . self::$_grammar['FWS'] . '))'; + self::$_grammar['qtext'] = '(?:' . self::$_grammar['NO-WS-CTL'] . + '|[\x21\x23-\x5B\x5D-\x7E])'; + self::$_grammar['qcontent'] = '(?:' . self::$_grammar['qtext'] . '|' . + self::$_grammar['quoted-pair'] . ')'; + self::$_grammar['quoted-string'] = '(?:' . self::$_grammar['CFWS'] . '?"' . + '(' . self::$_grammar['FWS'] . '?' . self::$_grammar['qcontent'] . ')*' . + self::$_grammar['FWS'] . '?"' . self::$_grammar['CFWS'] . '?)'; + self::$_grammar['atext'] = '[a-zA-Z0-9!#\$%&\'\*\+\-\/=\?\^_`\{\}\|~]'; + self::$_grammar['atom'] = '(?:' . self::$_grammar['CFWS'] . '?' . + self::$_grammar['atext'] . '+' . self::$_grammar['CFWS'] . '?)'; + self::$_grammar['dot-atom-text'] = '(?:' . self::$_grammar['atext'] . '+' . + '(\.' . self::$_grammar['atext'] . '+)*)'; + self::$_grammar['dot-atom'] = '(?:' . self::$_grammar['CFWS'] . '?' . + self::$_grammar['dot-atom-text'] . '+' . self::$_grammar['CFWS'] . '?)'; + self::$_grammar['word'] = '(?:' . self::$_grammar['atom'] . '|' . + self::$_grammar['quoted-string'] . ')'; + self::$_grammar['phrase'] = '(?:' . self::$_grammar['word'] . '+?)'; + self::$_grammar['no-fold-quote'] = '(?:"(?:' . self::$_grammar['qtext'] . + '|' . self::$_grammar['quoted-pair'] . ')*")'; + self::$_grammar['dtext'] = '(?:' . self::$_grammar['NO-WS-CTL'] . + '|[\x21-\x5A\x5E-\x7E])'; + self::$_grammar['no-fold-literal'] = '(?:\[(?:' . self::$_grammar['dtext'] . + '|' . self::$_grammar['quoted-pair'] . ')*\])'; + + //Message IDs + self::$_grammar['id-left'] = '(?:' . self::$_grammar['dot-atom-text'] . '|' . + self::$_grammar['no-fold-quote'] . ')'; + self::$_grammar['id-right'] = '(?:' . self::$_grammar['dot-atom-text'] . '|' . + self::$_grammar['no-fold-literal'] . ')'; + + //Addresses, mailboxes and paths + self::$_grammar['local-part'] = '(?:' . self::$_grammar['dot-atom'] . '|' . + self::$_grammar['quoted-string'] . ')'; + self::$_grammar['dcontent'] = '(?:' . self::$_grammar['dtext'] . '|' . + self::$_grammar['quoted-pair'] . ')'; + self::$_grammar['domain-literal'] = '(?:' . self::$_grammar['CFWS'] . '?\[(' . + self::$_grammar['FWS'] . '?' . self::$_grammar['dcontent'] . ')*?' . + self::$_grammar['FWS'] . '?\]' . self::$_grammar['CFWS'] . '?)'; + self::$_grammar['domain'] = '(?:' . self::$_grammar['dot-atom'] . '|' . + self::$_grammar['domain-literal'] . ')'; + self::$_grammar['addr-spec'] = '(?:' . self::$_grammar['local-part'] . '@' . + self::$_grammar['domain'] . ')'; + } + + /** + * Get the grammar defined for $name token. + * @param string $name execatly as written in the RFC + * @return string + */ + public function getDefinition($name) + { + if (array_key_exists($name, self::$_grammar)) + { + return self::$_grammar[$name]; + } + else + { + throw new Swift_RfcComplianceException( + "No such grammar '" . $name . "' defined." + ); + } + } + + /** + * Returns the tokens defined in RFC 2822 (and some related RFCs). + * @return array + */ + public function getGrammarDefinitions() + { + return self::$_grammar; + } + + /** + * Returns the current special characters used in the syntax which need to be escaped. + * @return array + */ + public function getSpecials() + { + return self::$_specials; + } + + /** + * Escape special characters in a string (convert to quoted-pairs). + * @param string $token + * @param string[] $include additonal chars to escape + * @param string[] $exclude chars from escaping + * @return string + */ + public function escapeSpecials($token, $include = array(), + $exclude = array()) + { + foreach ( + array_merge(array('\\'), array_diff(self::$_specials, $exclude), $include) as $char) + { + $token = str_replace($char, '\\' . $char, $token); + } + return $token; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php new file mode 100644 index 0000000..38fc40c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php @@ -0,0 +1,85 @@ +getName(), "\r\n"); + mb_internal_encoding($old); + return $newstring; + } + return parent::encodeString($string, $firstLineOffset, $maxLineLength); + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php new file mode 100644 index 0000000..9dbb713 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php @@ -0,0 +1,70 @@ +_safeMap[$byte] = chr($byte); + } + } + + /** + * Get the name of this encoding scheme. + * Returns the string 'Q'. + * @return string + */ + public function getName() + { + return 'Q'; + } + + /** + * Takes an unencoded string and produces a Q encoded string from it. + * @param string $string to encode + * @param int $firstLineOffset, optional + * @param int $maxLineLength, optional, 0 indicates the default of 76 chars + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, + $maxLineLength = 0, $charst = 'utf-8') + { + return str_replace(array(' ', '=20', "=\r\n"), array('_', '_', "\r\n"), + parent::encodeString($string, $firstLineOffset, $maxLineLength) + ); + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php new file mode 100644 index 0000000..13083bb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php @@ -0,0 +1,71 @@ +setGrammar($grammar); + } + + /** + * Set the character set used in this Header. + * @param string $charset + */ + public function setCharset($charset) + { + $this->clearCachedValueIf($charset != $this->_charset); + $this->_charset = $charset; + if (isset($this->_encoder)) + { + $this->_encoder->charsetChanged($charset); + } + } + + /** + * Get the character set used in this Header. + * @return string + */ + public function getCharset() + { + return $this->_charset; + } + + /** + * Set the language used in this Header. + * For example, for US English, 'en-us'. + * This can be unspecified. + * @param string $lang + */ + public function setLanguage($lang) + { + $this->clearCachedValueIf($this->_lang != $lang); + $this->_lang = $lang; + } + + /** + * Get the language used in this Header. + * @return string + */ + public function getLanguage() + { + return $this->_lang; + } + + /** + * Set the encoder used for encoding the header. + * @param Swift_Mime_HeaderEncoder $encoder + */ + public function setEncoder(Swift_Mime_HeaderEncoder $encoder) + { + $this->_encoder = $encoder; + $this->setCachedValue(null); + } + + /** + * Get the encoder used for encoding this Header. + * @return Swift_Mime_HeaderEncoder + */ + public function getEncoder() + { + return $this->_encoder; + } + + /** + * Set the grammar used for the header. + * @param Swift_Mime_Grammar $grammar + */ + public function setGrammar(Swift_Mime_Grammar $grammar) + { + $this->_grammar = $grammar; + $this->setCachedValue(null); + } + + /** + * Get the grammar used for this Header. + * @return Swift_Mime_Grammar + */ + public function getGrammar() + { + return $this->_grammar; + } + + /** + * Get the name of this header (e.g. charset). + * @return string + */ + public function getFieldName() + { + return $this->_name; + } + + /** + * Set the maximum length of lines in the header (excluding EOL). + * @param int $lineLength + */ + public function setMaxLineLength($lineLength) + { + $this->clearCachedValueIf($this->_lineLength != $lineLength); + $this->_lineLength = $lineLength; + } + + /** + * Get the maximum permitted length of lines in this Header. + * @return int + */ + public function getMaxLineLength() + { + return $this->_lineLength; + } + + /** + * Get this Header rendered as a RFC 2822 compliant string. + * @return string + * @throws Swift_RfcComplianceException + */ + public function toString() + { + return $this->_tokensToString($this->toTokens()); + } + + /** + * Returns a string representation of this object. + * + * @return string + * + * @see toString() + */ + public function __toString() + { + return $this->toString(); + } + + // -- Points of extension + + /** + * Set the name of this Header field. + * @param string $name + * @access protected + */ + protected function setFieldName($name) + { + $this->_name = $name; + } + + /** + * Produces a compliant, formatted RFC 2822 'phrase' based on the string given. + * @param Swift_Mime_Header $header + * @param string $string as displayed + * @param string $charset of the text + * @param Swift_Mime_HeaderEncoder $encoder + * @param boolean $shorten the first line to make remove for header name + * @return string + */ + protected function createPhrase(Swift_Mime_Header $header, $string, $charset, + Swift_Mime_HeaderEncoder $encoder = null, $shorten = false) + { + //Treat token as exactly what was given + $phraseStr = $string; + //If it's not valid + if (!preg_match('/^' . $this->getGrammar()->getDefinition('phrase') . '$/D', $phraseStr)) + { + // .. but it is just ascii text, try escaping some characters + // and make it a quoted-string + if (preg_match('/^' . $this->getGrammar()->getDefinition('text') . '*$/D', $phraseStr)) + { + $phraseStr = $this->getGrammar()->escapeSpecials( + $phraseStr, array('"'), $this->getGrammar()->getSpecials() + ); + $phraseStr = '"' . $phraseStr . '"'; + } + else // ... otherwise it needs encoding + { + //Determine space remaining on line if first line + if ($shorten) + { + $usedLength = strlen($header->getFieldName() . ': '); + } + else + { + $usedLength = 0; + } + $phraseStr = $this->encodeWords($header, $string, $usedLength); + } + } + + return $phraseStr; + } + + /** + * Encode needed word tokens within a string of input. + * @param string $input + * @param string $usedLength, optional + * @return string + */ + protected function encodeWords(Swift_Mime_Header $header, $input, + $usedLength = -1) + { + $value = ''; + + $tokens = $this->getEncodableWordTokens($input); + + foreach ($tokens as $token) + { + //See RFC 2822, Sect 2.2 (really 2.2 ??) + if ($this->tokenNeedsEncoding($token)) + { + //Don't encode starting WSP + $firstChar = substr($token, 0, 1); + switch($firstChar) + { + case ' ': + case "\t": + $value .= $firstChar; + $token = substr($token, 1); + } + + if (-1 == $usedLength) + { + $usedLength = strlen($header->getFieldName() . ': ') + strlen($value); + } + $value .= $this->getTokenAsEncodedWord($token, $usedLength); + + $header->setMaxLineLength(76); //Forefully override + } + else + { + $value .= $token; + } + } + + return $value; + } + + /** + * Test if a token needs to be encoded or not. + * @param string $token + * @return boolean + */ + protected function tokenNeedsEncoding($token) + { + return preg_match('~[\x00-\x08\x10-\x19\x7F-\xFF\r\n]~', $token); + } + + /** + * Splits a string into tokens in blocks of words which can be encoded quickly. + * @param string $string + * @return string[] + */ + protected function getEncodableWordTokens($string) + { + $tokens = array(); + + $encodedToken = ''; + //Split at all whitespace boundaries + foreach (preg_split('~(?=[\t ])~', $string) as $token) + { + if ($this->tokenNeedsEncoding($token)) + { + $encodedToken .= $token; + } + else + { + if (strlen($encodedToken) > 0) + { + $tokens[] = $encodedToken; + $encodedToken = ''; + } + $tokens[] = $token; + } + } + if (strlen($encodedToken)) + { + $tokens[] = $encodedToken; + } + + return $tokens; + } + + /** + * Get a token as an encoded word for safe insertion into headers. + * @param string $token to encode + * @param int $firstLineOffset, optional + * @return string + */ + protected function getTokenAsEncodedWord($token, $firstLineOffset = 0) + { + //Adjust $firstLineOffset to account for space needed for syntax + $charsetDecl = $this->_charset; + if (isset($this->_lang)) + { + $charsetDecl .= '*' . $this->_lang; + } + $encodingWrapperLength = strlen( + '=?' . $charsetDecl . '?' . $this->_encoder->getName() . '??=' + ); + + if ($firstLineOffset >= 75) //Does this logic need to be here? + { + $firstLineOffset = 0; + } + + $encodedTextLines = explode("\r\n", + $this->_encoder->encodeString( + $token, $firstLineOffset, 75 - $encodingWrapperLength, $this->_charset + ) + ); + + if (strtolower($this->_charset) !== 'iso-2022-jp') // special encoding for iso-2022-jp using mb_encode_mimeheader + { + foreach ($encodedTextLines as $lineNum => $line) + { + $encodedTextLines[$lineNum] = '=?' . $charsetDecl . + '?' . $this->_encoder->getName() . + '?' . $line . '?='; + } + } + + return implode("\r\n ", $encodedTextLines); + } + + /** + * Generates tokens from the given string which include CRLF as individual tokens. + * @param string $token + * @return string[] + * @access protected + */ + protected function generateTokenLines($token) + { + return preg_split('~(\r\n)~', $token, -1, PREG_SPLIT_DELIM_CAPTURE); + } + + /** + * Set a value into the cache. + * @param string $value + * @access protected + */ + protected function setCachedValue($value) + { + $this->_cachedValue = $value; + } + + /** + * Get the value in the cache. + * @return string + * @access protected + */ + protected function getCachedValue() + { + return $this->_cachedValue; + } + + /** + * Clear the cached value if $condition is met. + * @param boolean $condition + * @access protected + */ + protected function clearCachedValueIf($condition) + { + if ($condition) + { + $this->setCachedValue(null); + } + } + + // -- Private methods + + /** + * Generate a list of all tokens in the final header. + * @param string $string The string to tokenize + * @return array An array of tokens as strings + * @access protected + */ + protected function toTokens($string = null) + { + if (is_null($string)) + { + $string = $this->getFieldBody(); + } + + $tokens = array(); + + //Generate atoms; split at all invisible boundaries followed by WSP + foreach (preg_split('~(?=[ \t])~', $string) as $token) + { + $tokens = array_merge($tokens, $this->generateTokenLines($token)); + } + + return $tokens; + } + + /** + * Takes an array of tokens which appear in the header and turns them into + * an RFC 2822 compliant string, adding FWSP where needed. + * @param string[] $tokens + * @return string + * @access private + */ + private function _tokensToString(array $tokens) + { + $lineCount = 0; + $headerLines = array(); + $headerLines[] = $this->_name . ': '; + $currentLine =& $headerLines[$lineCount++]; + + //Build all tokens back into compliant header + foreach ($tokens as $i => $token) + { + //Line longer than specified maximum or token was just a new line + if (("\r\n" == $token) || + ($i > 0 && strlen($currentLine . $token) > $this->_lineLength) + && 0 < strlen($currentLine)) + { + $headerLines[] = ''; + $currentLine =& $headerLines[$lineCount++]; + } + + //Append token to the line + if ("\r\n" != $token) + { + $currentLine .= $token; + } + } + + //Implode with FWS (RFC 2822, 2.2.3) + return implode("\r\n", $headerLines) . "\r\n"; + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php new file mode 100644 index 0000000..bb345fd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php @@ -0,0 +1,119 @@ + + * + *
    + * @param string $name of Header + * @param Swift_Mime_Grammar $grammar + */ + public function __construct($name, Swift_Mime_Grammar $grammar) + { + $this->setFieldName($name); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * @return int + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + */ + public function getFieldType() + { + return self::TYPE_DATE; + } + + /** + * Set the model for the field body. + * This method takes a UNIX timestamp. + * @param int $model + */ + public function setFieldBodyModel($model) + { + $this->setTimestamp($model); + } + + /** + * Get the model for the field body. + * This method returns a UNIX timestamp. + * @return mixed + */ + public function getFieldBodyModel() + { + return $this->getTimestamp(); + } + + /** + * Get the UNIX timestamp of the Date in this Header. + * @return int + */ + public function getTimestamp() + { + return $this->_timestamp; + } + + /** + * Set the UNIX timestamp of the Date in this Header. + * @param int $timestamp + */ + public function setTimestamp($timestamp) + { + if (!is_null($timestamp)) + { + $timestamp = (int) $timestamp; + } + $this->clearCachedValueIf($this->_timestamp != $timestamp); + $this->_timestamp = $timestamp; + } + + /** + * Get the string value of the body in this Header. + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * @return string + * @see toString() + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) + { + if (isset($this->_timestamp)) + { + $this->setCachedValue(date('r', $this->_timestamp)); + } + } + return $this->getCachedValue(); + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php new file mode 100644 index 0000000..0304175 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php @@ -0,0 +1,166 @@ +setFieldName($name); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * @return int + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + */ + public function getFieldType() + { + return self::TYPE_ID; + } + + /** + * Set the model for the field body. + * This method takes a string ID, or an array of IDs + * @param mixed $model + * @throws Swift_RfcComplianceException + */ + public function setFieldBodyModel($model) + { + $this->setId($model); + } + + /** + * Get the model for the field body. + * This method returns an array of IDs + * @return array + */ + public function getFieldBodyModel() + { + return $this->getIds(); + } + + /** + * Set the ID used in the value of this header. + * @param string|array $id + * @throws Swift_RfcComplianceException + */ + public function setId($id) + { + $this->setIds(is_array($id) ? $id : array($id)); + } + + /** + * Get the ID used in the value of this Header. + * If multiple IDs are set only the first is returned. + * @return string + */ + public function getId() + { + if (count($this->_ids) > 0) + { + return $this->_ids[0]; + } + } + + /** + * Set a collection of IDs to use in the value of this Header. + * @param string[] $ids + * @throws Swift_RfcComplianceException + */ + public function setIds(array $ids) + { + $actualIds = array(); + + foreach ($ids as $id) + { + $this->_assertValidId($id); + $actualIds[] = $id; + } + + $this->clearCachedValueIf($this->_ids != $actualIds); + $this->_ids = $actualIds; + } + + /** + * Get the list of IDs used in this Header. + * @return string[] + */ + public function getIds() + { + return $this->_ids; + } + + /** + * Get the string value of the body in this Header. + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * @return string + * @see toString() + * @throws Swift_RfcComplianceException + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) + { + $angleAddrs = array(); + + foreach ($this->_ids as $id) + { + $angleAddrs[] = '<' . $id . '>'; + } + + $this->setCachedValue(implode(' ', $angleAddrs)); + } + return $this->getCachedValue(); + } + + /** + * Throws an Exception if the id passed does not comply with RFC 2822. + * @param string $id + * @throws Swift_RfcComplianceException + */ + private function _assertValidId($id) + { + if (!preg_match( + '/^' . $this->getGrammar()->getDefinition('id-left') . '@' . + $this->getGrammar()->getDefinition('id-right') . '$/D', + $id + )) + { + throw new Swift_RfcComplianceException( + 'Invalid ID given <' . $id . '>' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php new file mode 100644 index 0000000..0664560 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php @@ -0,0 +1,326 @@ +setFieldName($name); + $this->setEncoder($encoder); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * @return int + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + */ + public function getFieldType() + { + return self::TYPE_MAILBOX; + } + + /** + * Set the model for the field body. + * This method takes a string, or an array of addresses. + * @param mixed $model + * @throws Swift_RfcComplianceException + */ + public function setFieldBodyModel($model) + { + $this->setNameAddresses($model); + } + + /** + * Get the model for the field body. + * This method returns an associative array like {@link getNameAddresses()} + * @return array + * @throws Swift_RfcComplianceException + */ + public function getFieldBodyModel() + { + return $this->getNameAddresses(); + } + + /** + * Set a list of mailboxes to be shown in this Header. + * The mailboxes can be a simple array of addresses, or an array of + * key=>value pairs where (email => personalName). + * Example: + * + * setNameAddresses(array( + * 'chris@swiftmailer.org' => 'Chris Corbyn', + * 'mark@swiftmailer.org' //No associated personal name + * )); + * ?> + * + * @param string|string[] $mailboxes + * @throws Swift_RfcComplianceException + * @see __construct() + * @see setAddresses() + * @see setValue() + */ + public function setNameAddresses($mailboxes) + { + $this->_mailboxes = $this->normalizeMailboxes((array) $mailboxes); + $this->setCachedValue(null); //Clear any cached value + } + + /** + * Get the full mailbox list of this Header as an array of valid RFC 2822 strings. + * Example: + * + * 'Chris Corbyn', + * 'mark@swiftmailer.org' => 'Mark Corbyn') + * ); + * print_r($header->getNameAddressStrings()); + * // array ( + * // 0 => Chris Corbyn , + * // 1 => Mark Corbyn + * // ) + * ?> + * + * @return string[] + * @throws Swift_RfcComplianceException + * @see getNameAddresses() + * @see toString() + */ + public function getNameAddressStrings() + { + return $this->_createNameAddressStrings($this->getNameAddresses()); + } + + /** + * Get all mailboxes in this Header as key=>value pairs. + * The key is the address and the value is the name (or null if none set). + * Example: + * + * 'Chris Corbyn', + * 'mark@swiftmailer.org' => 'Mark Corbyn') + * ); + * print_r($header->getNameAddresses()); + * // array ( + * // chris@swiftmailer.org => Chris Corbyn, + * // mark@swiftmailer.org => Mark Corbyn + * // ) + * ?> + * + * @return string[] + * @see getAddresses() + * @see getNameAddressStrings() + */ + public function getNameAddresses() + { + return $this->_mailboxes; + } + + /** + * Makes this Header represent a list of plain email addresses with no names. + * Example: + * + * setAddresses( + * array('one@domain.tld', 'two@domain.tld', 'three@domain.tld') + * ); + * ?> + * + * @param string[] $addresses + * @throws Swift_RfcComplianceException + * @see setNameAddresses() + * @see setValue() + */ + public function setAddresses($addresses) + { + $this->setNameAddresses(array_values((array) $addresses)); + } + + /** + * Get all email addresses in this Header. + * @return string[] + * @see getNameAddresses() + */ + public function getAddresses() + { + return array_keys($this->_mailboxes); + } + + /** + * Remove one or more addresses from this Header. + * @param string|string[] $addresses + */ + public function removeAddresses($addresses) + { + $this->setCachedValue(null); + foreach ((array) $addresses as $address) + { + unset($this->_mailboxes[$address]); + } + } + + /** + * Get the string value of the body in this Header. + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * @return string + * @throws Swift_RfcComplianceException + * @see toString() + */ + public function getFieldBody() + { + //Compute the string value of the header only if needed + if (is_null($this->getCachedValue())) + { + $this->setCachedValue($this->createMailboxListString($this->_mailboxes)); + } + return $this->getCachedValue(); + } + + // -- Points of extension + + /** + * Normalizes a user-input list of mailboxes into consistent key=>value pairs. + * @param string[] $mailboxes + * @return string[] + * @access protected + */ + protected function normalizeMailboxes(array $mailboxes) + { + $actualMailboxes = array(); + + foreach ($mailboxes as $key => $value) + { + if (is_string($key)) //key is email addr + { + $address = $key; + $name = $value; + } + else + { + $address = $value; + $name = null; + } + $this->_assertValidAddress($address); + $actualMailboxes[$address] = $name; + } + + return $actualMailboxes; + } + + /** + * Produces a compliant, formatted display-name based on the string given. + * @param string $displayName as displayed + * @param boolean $shorten the first line to make remove for header name + * @return string + * @access protected + */ + protected function createDisplayNameString($displayName, $shorten = false) + { + return $this->createPhrase($this, $displayName, + $this->getCharset(), $this->getEncoder(), $shorten + ); + } + + /** + * Creates a string form of all the mailboxes in the passed array. + * @param string[] $mailboxes + * @return string + * @throws Swift_RfcComplianceException + * @access protected + */ + protected function createMailboxListString(array $mailboxes) + { + return implode(', ', $this->_createNameAddressStrings($mailboxes)); + } + + /** + * Redefine the encoding requirements for mailboxes. Commas and semicolons are used to separate + * multiple addresses, and should therefore be encoded + * @param string $token + * @return boolean + */ + protected function tokenNeedsEncoding($token) + { + return preg_match('/[,;]/', $token) || parent::tokenNeedsEncoding($token); + } + + // -- Private methods + + /** + * Return an array of strings conforming the the name-addr spec of RFC 2822. + * @param string[] $mailboxes + * @return string[] + * @access private + */ + private function _createNameAddressStrings(array $mailboxes) + { + $strings = array(); + + foreach ($mailboxes as $email => $name) + { + $mailboxStr = $email; + if (!is_null($name)) + { + $nameStr = $this->createDisplayNameString($name, empty($strings)); + $mailboxStr = $nameStr . ' <' . $mailboxStr . '>'; + } + $strings[] = $mailboxStr; + } + + return $strings; + } + + /** + * Throws an Exception if the address passed does not comply with RFC 2822. + * @param string $address + * @throws Swift_RfcComplianceException If invalid. + * @access private + */ + private function _assertValidAddress($address) + { + if (!preg_match('/^' . $this->getGrammar()->getDefinition('addr-spec') . '$/D', + $address)) + { + throw new Swift_RfcComplianceException( + 'Address in mailbox given [' . $address . + '] does not comply with RFC 2822, 3.6.2.' + ); + } + } + +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php new file mode 100644 index 0000000..0a31b2d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php @@ -0,0 +1,268 @@ +_paramEncoder = $paramEncoder; + } + + /** + * Get the type of Header that this instance represents. + * @return int + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + */ + public function getFieldType() + { + return self::TYPE_PARAMETERIZED; + } + + /** + * Set the character set used in this Header. + * @param string $charset + */ + public function setCharset($charset) + { + parent::setCharset($charset); + if (isset($this->_paramEncoder)) + { + $this->_paramEncoder->charsetChanged($charset); + } + } + + /** + * Set the value of $parameter. + * @param string $parameter + * @param string $value + */ + public function setParameter($parameter, $value) + { + $this->setParameters(array_merge($this->getParameters(), array($parameter => $value))); + } + + /** + * Get the value of $parameter. + * @return string + */ + public function getParameter($parameter) + { + $params = $this->getParameters(); + return array_key_exists($parameter, $params) + ? $params[$parameter] + : null; + } + + /** + * Set an associative array of parameter names mapped to values. + * @param string[] + */ + public function setParameters(array $parameters) + { + $this->clearCa